package com.iot08.api.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iot08.api.config.redis.RedisClusterService;
import com.iot08.api.config.redis.RedisVideoService;
import com.iot08.api.constant.Constant;
import com.iot08.api.service.supplement.KafkaService;
import com.iot08.api.service.supplement.TCPService;
import com.iot08.api.utils.StringTool;
import com.iot08.api.utils.buckettool.BaseBucketTool;
import com.iot08.api.utils.ContentSecurityUtil;
import com.iot08.api.utils.SpringContextUtil;
import com.iot08.api.utils.request.DESEncryptTools;
import com.iot08.api.utils.sw.RtcTokenBuilder;
import com.iot08.api.vo.request.*;
import com.iot08.api.vo.request.base.BaseAccountReqVO;
import com.iot08.common.config.mongodb.query.UpdateVO;
import com.iot08.common.config.redis.RedisService;
import com.iot08.common.constant.*;
import com.iot08.common.entity.app.*;
import com.iot08.common.service.base.BaseService;
import com.iot08.common.utils.*;
import com.iot08.common.utils.response.ResponseEnum;
import com.iot08.common.utils.response.ResponseMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ThreadUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.net.ResponseCache;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0
 * @Description: 账号相关业务处理
 * @author: Rock
 * @time: 2020/11/3 16:25
 */
@Slf4j
@Service
public class AccountService extends BaseService<Account> {

    @Resource
    private RedisClusterService redisClusterService;
    //视频
    @Resource
    private RedisVideoService redisVideoService;

    @Resource
    private RedisService redisService;

    @Resource
    private DeviceService deviceService;

    @Resource
    private DeviceAttentionService deviceAttentionService;

    @Resource
    private DeviceVideoService deviceVideoService;

    @Resource
    private ChatGroupService chatGroupService;

    @Resource
    private SingleChatGroupService singleChatGroupService;

    @Resource
    private BaseBucketTool baseBucketTool;

    @Resource
    private WxVipCnsService wxVipCnsService;

    @Resource
    private OpenidConfigService openidConfigService;

    @Resource
    private VerificationCodeUtil verificationCodeUtil;

    @Resource
    private UserKeyActionService userKeyActionService;

    @Resource
    private KafkaService kafkaService;

    @Resource
    private TCPService tcpService;

    @Resource
    private VipMemberService vipMemberService;

    @Resource
    private LoginOperateLogService loginOperateLogService;

    @Resource
    private OperateLogService operateLogService;

    @Resource
    private WechatContactService wechatContactService;

    @Resource
    private ModelsService modelsService;


    @Value("${vm.app.key}")
    private String vmAppKey;
    @Value("${vm.app.connection.address}")
    private String vmAppConnectionAddress;


    @Value("${jf.sjVideo.app.key:}")
    private String jf_SJAppKey;

    @Value("${jf.sjVideo.ASRWatch.secret_key:}")
    private String jf_SJASRWatchSecretKey;

    @Value("${jf.app.key}")
    private String jfAppKey;
    //菊风的APP 连接地址
    @Value("${jf.app.connection.address}")
    private String jfAppConnectionAddress;

    //声网
    @Value("${sw.watch.appid}")
    private String swAPP_ID;
    @Value("${sw.watch.customer_id}")
    private String swCUSTOMER_ID;
    @Value("${sw.watch.customer_certificate}")
    private String swCUSTOMER_CERTIFICATE;
    @Value("${sw.watch.app_certificate}")
    private String swAPP_CERTIFICATE;

    // 拨打视频等待时间 /秒
    @Value("${video.VIDEO_WAIT_TIME}")
    private int VIDEO_WAIT_TIME;
    // 单次通话时长/秒 设备小热的等待时间 (限制一次拨打的时长)
    @Value("${video.LIMIT_ONE_CALL_VIDEO_TIME}")
    private int LIMIT_ONE_CALL_VIDEO_TIME;

    @Value("${feedback.emailMap:{}}")
    private String feedbackEmailMapStr;

    /**
     * 获取国家编号
     *
     * @time: 2/23/22 3:54 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap getCountry() {
        JSONObject res = new JSONObject();
        String countryListStr = "[\"中国:86\",\"英国:44\",\"美国:1\",\"加拿大:1\",\"法国:33\",\"德国:49\",\"中国香港:852\",\"中国澳门:853\",\"中国台湾省:886\",\"安哥拉:244\",\"阿富汗:93\",\"阿根廷:54\",\"澳大利亚:61\",\"奥地利:43\",\"孟加拉国:880\",\"白俄罗斯:375\",\"比利时:32\",\"巴西:55\",\"缅甸:95\",\"中非共和国:236\",\"智利:56\",\"哥伦比亚:57\",\"刚果:242\",\"古巴:53\",\"丹麦:45\",\"厄瓜多尔:593\",\"埃及:20\",\"芬兰:358\",\"希腊:30\",\"匈牙利:36\",\"冰岛:354\",\"印度:91\",\"印度尼西亚:62\",\"伊朗:98\",\"伊拉克:964\",\"爱尔兰:353\",\"以色列:972\",\"意大利:39\",\"牙买加:1876\",\"日本:81\",\"约旦:962\",\"肯尼亚:254\",\"韩国:82\",\"利比里亚:231\",\"利比亚:218\",\"马达加斯加:261\",\"马拉维:265\",\"马来西亚:60\",\"马尔代夫:960\",\"墨西哥:52\",\"蒙古:976\",\"荷兰:31\",\"新西兰:64\",\"尼日利亚:234\",\"朝鲜:850\",\"挪威:47\",\"巴基斯坦:92\",\"巴拿马:507\",\"秘鲁:51\",\"菲律宾:63\",\"波兰:48\",\"葡萄牙:351\",\"俄罗斯:7\",\"新加坡:65\",\"西班牙:34\",\"瑞典:46\",\"瑞士:41\",\"叙利亚:963\",\"泰国:66\",\"土耳其:90\",\"阿拉伯联合酋长国:971\",\"乌拉圭:598\",\"越南:84\"]";
        JSONArray tempArr = JSONArray.parseArray(countryListStr);
        JSONArray countrysArr = new JSONArray();
        for (int i = 0; i < tempArr.size(); i++) {
            String item = tempArr.getString(i);
            JSONObject tempOB = new JSONObject();
            tempOB.put("country", item.split(":")[0]);
            tempOB.put("country_code", Integer.valueOf(item.split(":")[1]));
            countrysArr.add(tempOB);
        }
        res.put("countrys", countrysArr);
        return ResponseMap.success(res);
    }

    /**
     * 创建账号
     *
     * @param type         账号登录方式 1:qq , 2:微信, 3:imei, 4:公众号, 5:手机号, 6:邮箱, 7:apple登录
     * @param val          标识 当type 不同是含义不同
     * @param name
     * @param country_code 国家编码
     * @time: 2021/1/6 15:47
     * @author: Rock
     * @version 1.0
     */
    public Account createAccount(int type, String val, String name, Integer country_code) {
        Account account = new Account();
        if (type == 1) {
            account.setQqid(val);
        } else if (type == 2) {
            account.setWxid(val);
        } else if (type == 3) {
            account.setImei(val);
        } else if (type == 4) {
            account.setWxid(val);
            account.setWxgzIdent(4);
        } else if (type == 5) {
            account.setPhone(val);
        } else if (type == 6) {
            account.setEmail(val);
        } else if (type == 7) {
            account.setAppleid(val);
        } else if (type == 8) {
            //词典笔
            //设置默认头像
            account.setImage("");
            account.setId(val);
        } else {
            return null;
        }
        if (StringUtil.isNotBlank(name)) {
            account.setName(name);
        }
        if (country_code != null) {
            account.setCountryCode(country_code);
        }
        if (account.getId() == null) {
            //没有则初始化一个
            account.setId(UUIDGenerator.generate());
        }
        double time = DateUtil.nowTimestampToDouble();
        account.setTimestamp(time);
        account.setDeleteNotificationtime(time);
        account.setRegisterDate(new Date());
        account.setLoginDate(new Date());
        return this.save(account);
    }

    /**
     * @param [type, val, name, country_code, password]
     * @return com.iot08.common.entity.app.Account
     * @description 保存账户信息
     * @author cwj
     * @date @time 2025/3/26 14:20
     **/
    public Account saveAccount(int type, String val, String name, Integer country_code, String password, String email) {
        Account account = new Account();
        if (type == 1) {
            account.setQqid(val);
        } else if (type == 2) {
            account.setWxid(val);
        } else if (type == 3) {
            account.setImei(val);
        } else if (type == 4) {
            account.setWxid(val);
            account.setWxgzIdent(4);
        } else if (type == 5) {
            account.setPhone(val);
        } else if (type == 6) {
            account.setEmail(val);
        } else if (type == 7) {
            account.setAppleid(val);
        } else if (type == 8) {
            //词典笔
            //设置默认头像
            account.setImage("");
            account.setId(val);
        } else {
            return null;
        }
        if (StringUtil.isNotBlank(name)) {
            account.setName(name);
        }
        if (country_code != null) {
            account.setCountryCode(country_code);
        }
        if (account.getId() == null) {
            //没有则初始化一个
            account.setId(UUIDGenerator.generate());
        }
        if (!StringUtils.isEmpty(password)) {
            account.setPwd(password);
        }
        if (StringUtils.isNotEmpty(email)) {
            account.setEmail(email);
        }
        double time = DateUtil.nowTimestampToDouble();
        account.setTimestamp(time);
        account.setDeleteNotificationtime(time);
        account.setRegisterDate(new Date());
        account.setLoginDate(new Date());
        return this.save(account);
    }


    /**
     * @param [type, val, name, country_code]
     * @return com.iot08.common.entity.app.Account
     * @description 保存第三方登录用户
     * @author cwj
     * @date @time 2025/3/31 14:33
     **/
    public Account saveThirdAccount(int type, String val, String name, Integer country_code, String unionid, String wxName, Integer wxStatus, String loginType) {
        Account account = new Account();
        if (type == 1) {
            account.setQqid(val);
        } else if (type == 2) {
            account.setWxid(val);
        } else if (type == 3) {
            account.setImei(val);
        } else if (type == 4) {
            account.setWxid(val);
            account.setWxgzIdent(4);
        } else if (type == 5) {
            account.setPhone(val);
        } else if (type == 6) {
            account.setEmail(val);
        } else if (type == 7) {
            account.setAppleid(val);
        } else if (type == 8) {
            //词典笔
            //设置默认头像
            account.setImage("");
            account.setId(val);
        } else {
            return null;
        }
        if (StringUtil.isNotBlank(name)) {
            account.setName(name);
        }
        if (country_code != null) {
            account.setCountryCode(country_code);
        }
        if (account.getId() == null) {
            //没有则初始化一个
            account.setId(UUIDGenerator.generate());
        }
        if (StringUtils.isNotEmpty(unionid)) {
            account.setUnionid(unionid);
        }
        if (StringUtils.isNotEmpty(wxName)) {
            account.setWxName(wxName);
        }
        if (!ObjectUtils.isEmpty(wxStatus)) {
            account.setWx_status(wxStatus);
        }
        if (StringUtils.isNotEmpty(loginType)) {
            account.setLoginType(loginType);
        }
        double time = DateUtil.nowTimestampToDouble();
        account.setTimestamp(time);
        account.setDeleteNotificationtime(time);
        account.setRegisterDate(new Date());
        account.setLoginDate(new Date());
        return this.save(account);
    }

    /**
     * 注册邮箱时 获取 注册码
     *
     * @param vo
     * @param needDecryption 是否需要加密
     * @return
     */
    public ResponseMap getEmailCode(AccountsRegisBodyFormVO vo, boolean needDecryption) {
        String email = vo.getEmail();
        if (email != null) {
            if (needDecryption) {
                email = DESEncryptTools.decode(email);
                if (email == null) {
                    return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
                }
            }
            //校验邮箱格式
            if (VerificationCodeUtil.isEmail(email)) {
                //邮箱登录
                Query query = new Query();
                if (this.count(query.addCriteria(Criteria.where("email").is(vo.getEmail()))) >= 1) {
                    //邮箱重复
                    return ResponseMap.fail(ResponseEnum.STATUS5012.getCode(), ResponseEnum.STATUS5012.getMsg());
                }
                // 获取验证码
                String verificationCode = verificationCodeUtil.getVerificationCode(email, 1, null);
                log.info("邮箱:{} 发送验证码:{}", email, verificationCode);
                if (verificationCode == null) {
                    return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
                }
                redisClusterService.setex(CacheEnum.VCODE.getKey() + email, CacheEnum.VCODE.getExpire(), verificationCode);
                JSONObject res = new JSONObject();
                return ResponseMap.success(res);
            }
        }
        return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
    }

    /**
     * 注册
     *
     * @param vo
     * @param needDecryption 是否需要加密
     * @time: 4/2/22 9:57 AM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap register(AccountsRegisBodyFormVO vo, boolean needDecryption) {
        Integer country_code = vo.getCountry_code();
        String identStr;
        String qStr;
        Integer accountType = 5; //手机
        if (country_code != null) {
            //手机
            identStr = vo.getPhone();
            qStr = "phone";
            accountType = 5;
        } else {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), ResponseEnum.STATUS400.getMsg());
        }
        //检查参数有没有传
        if (StringUtils.isAnyBlank(vo.getPhone(), vo.getAuth_code())) {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), ResponseEnum.STATUS400.getMsg());
        }
        if (needDecryption) {
            identStr = DESEncryptTools.decode(identStr);
        }
        //验证验证码是否正确
        String codeKey = Constant.SMS_KEY_PREFIX + vo.getUuid();
        if (!ObjectUtils.isEmpty(vo.getSendCodeWay()) && Integer.valueOf(1).equals(vo.getSendCodeWay())) {
            if (StringUtils.isEmpty(vo.getEmail())) {
                return ResponseMap.fail("参数缺失，用Email方式发送的验证码，却没传email值，无法注册");
            }
            codeKey += vo.getEmail();
        } else {
            codeKey += identStr;
        }
        //检查验证码
        ResponseMap status400 = checkVerifyCode(vo.getAuth_code(), codeKey);
        if (status400 != null) return status400;

       /* else{
            //邮箱
            identStr = vo.getEmail();
            qStr = "email";
            accountType = 6; //邮箱
        }*/

        //phone 或者 email 和 password是否需要 3DES 解密一下
        String password = vo.getPassword();
        if (needDecryption) {
            //password = DESEncryptTools.decode(password);
            if (identStr == null || password == null) {
                return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
            }
        }

        //赛诺目前只有 email 登录时才需要验证 验证码
       /* if(accountType == 6){
            //校验验证码
            String vcode = redisClusterService.get(CacheEnum.VCODE.getKey() + identStr);
            if (!vo.getAuth_code().equals(vcode)){
                return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
            }else{
                //验证成功后 验证码需要失效
                redisClusterService.del(CacheEnum.VCODE.getKey() + identStr);
            }
        }*/

        long count = 0;
        Query query = new Query();
        //if(accountType == 5){
        //手机
        count = this.count(query.addCriteria(Criteria.where(qStr).is(identStr).and("country_code").is(country_code)));
        if (count != 0) {
            return ResponseMap.fail(ResponseEnum.STATUS5010.getCode(), ResponseEnum.STATUS5010.getMsg());
        }

       /* }else{
            //邮箱
            count = this.count(query.addCriteria(Criteria.where(qStr).is(identStr)));
            if(count != 0){
                return ResponseMap.fail(ResponseEnum.STATUS5012.getCode(), ResponseEnum.STATUS5012.getMsg());
            }
        }*/

        Account account = saveAccount(accountType, identStr, null, country_code, password, vo.getEmail());
        // createAccount(accountType, identStr, null, country_code);
        //设置用户密码
        //account.setPwd(password);
        //this.save(account);

        JSONObject res = new JSONObject();
        res.put("openid", account.getId());
        res.put("accesstoken", getAccesstoken(account));
        //userKeyActionService.add(1,account.getId(), "用户注册");
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), "", 0,
                "登录", 1, vo.toString(), "AccountService.register", "用户注册成功");
        //验证成功删除缓存的验证码
        redisService.del(codeKey);
        //赠送一周的vip
        vipMemberService.presentVipMember(account.getId(), (long) (7 * 24 * 60 * 60));
        return ResponseMap.success(res);
    }

    /**
     * @param [vo, codeKey]
     * @return com.iot08.common.utils.response.ResponseMap
     * @description 检查验证码的正确性
     * @author cwj
     * @date @time 2024/11/20 11:02
     **/
    private ResponseMap checkVerifyCode(String authCode, String codeKey) {
        if (!redisService.exists(codeKey)) {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.request.parameterError.checkCode.past.due");
        }
        if (!authCode.equals(redisService.get(codeKey))) {
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.request.parameterError.checkCode.error");
        }
        return null;
    }

    /**
     * 手机/邮箱登录账号
     *
     * @param vo
     * @param needDecryption
     * @time: 4/2/22 2:57 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap login(AccountsReqBodyFormVO vo, boolean needDecryption) {
        log.info("登录用户的账户信息：{}", vo.getOpenid());
        Query query = new Query();
        Account account = null;
        String login_type = null;
        String phone = vo.getPhone();
        String email = vo.getEmail();
        if (phone != null) {
            login_type = "phone";
            int country_code = vo.getCountry_code();
            if (needDecryption) {
                phone = DESEncryptTools.decode(phone);
                if (phone == null) {
                    log.error("参数传递错误，登录时未传电话号码");
                    return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
                }
            }
            //手机号登录
            account = this.findOne(query.addCriteria(Criteria.where("phone").is(phone).and("country_code").is(country_code)));
            if (account == null) {
                log.error("登录用户不存在，请先注册，登录用户的电话号码为：{}", phone);
                return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
            }
        } else if (email != null) {
            login_type = "email";
            if (needDecryption) {
                email = DESEncryptTools.decode(email);
                if (email == null) {
                    return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
                }
            }
            //邮箱登录
            account = this.findOne(query.addCriteria(Criteria.where("email").is(email)));
            if (account == null) {
                return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
            }
        }
        if ("1".equals(account.getForbiddenStatus())) {
            //账号已被系统禁用，如有疑问，请联系客服人员
            loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), phone, "手机号", 1,
                    "登录", 0, vo.toString(), "AccountService.login", "用户账号被禁用，登录失败");
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.accountForbidden");
        }
        //验证密码
        String password = vo.getPassword();
        if (needDecryption) {
            //password = DESEncryptTools.decode(password);
            if (password == null) {
                loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), phone, "手机号", 1,
                        "登录", 0, vo.toString(), "AccountService.login", "登录失败，登录时为输入密码");
                return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
            }
        }
        if (!account.getPwd().equals(password)) {
            loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), phone, "手机号", 1,
                    "登录", 0, vo.toString(), "AccountService.login", "登录失败，密码不正确");
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), ResponseEnum.STATUS403.getMsg());
        }

        //更新登录时间等
        Update updateA = new Update();
        updateA.set("mobile_type", vo.getMobile_type());
        updateA.set("old_mobile_type", account.getMobileType());
        updateA.set("login_type", login_type);
        updateA.set("login_date", new Date());
        String oldRegistrationID = account.getRegistrationID();
        int tempNum = 0;
        if (vo.getRegistrationID() != null) {
            updateA.set("registrationID", vo.getRegistrationID());
            updateA.set("oldRegistrationID", oldRegistrationID);
            //原本的不为空, 且新旧不等 是为异地
            if (StringUtils.isNotBlank(oldRegistrationID) && !vo.getRegistrationID().equals(oldRegistrationID)) {
                tempNum = 1;
            }
        } else {
            if (account.getAccesstoken() != null) {
                String redisAccesstokenV = redisClusterService.get(CacheEnum.UA.getKey() + account.getAccesstoken());
                if (redisAccesstokenV != null && account.getId().equals(redisAccesstokenV)) {
                    //系统登出一次，去掉上次登录的痕迹
                    this.delAccessTokenCache(account.getAccesstoken());
                    this.logout(account.getId());
                    //tempNum = 2;
                }
            }
        }

        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String iosProduction = req.getHeader("iosProduction");
        if (iosProduction != null) {
            updateA.set("iosProduction", iosProduction);
        }

        this.updateById(account.getId(), updateA);
        //重新生成 accesstoken
        String OldAccesstoken = account.getAccesstoken();
        String accesstoken = updateAccesstoken(new Update(), account.getId(), OldAccesstoken, Constant.INVALID);
        ResponseMap res = ResponseMap.success();
        res.put("openid", account.getId());
        res.put("accesstoken", accesstoken);
        if (tempNum > 0) {
            //推送异地登录
            //推送异地登录
            JSONObject loginOffLineOB = new JSONObject();
            String msgTitle = "您的账号已在其他设备登录";
            loginOffLineOB.put("type", "accountMessage");
            JSONObject messageOB = new JSONObject();
            messageOB.put("type", "LoginOffLine");
            messageOB.put("timestamp", DateUtil.nowTimestampToLong());
            messageOB.put("content", msgTitle);
            messageOB.put("msgTitle", msgTitle);
            messageOB.put("accesstoken", OldAccesstoken);
            loginOffLineOB.put("message", messageOB);
            kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, account.getId(), loginOffLineOB);
        }
        //userKeyActionService.add(3,account.getId(), "用户登录");
        //记录日志
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), phone, "手机号", 1,
                "登录", 1, vo.toString(), "AccountService.login", "登录成功");
        return res;
    }

    /**
     * 第三方登录
     *
     * @param vo
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap thirdlogin(AccountsReqBodyFormVO vo, boolean needDecryption) {
        Integer type = vo.getType();
        String updateId = vo.getId();
        String name = vo.getName();
        String registrationID = vo.getRegistrationID();

        Query query = new Query();
        Account account = null;
        String des = "";
        JSONObject res = new JSONObject();
        Update aUpdate = new Update();
        if (type == 1) {
            // QQ (赛诺的目前已经没有QQ登录了)
            account = this.findOne(query.addCriteria(Criteria.where("qqid").is(updateId)));
            if (account != null) {
                aUpdate.set("qq_name", name);
            } else {
                account = this.createAccount(type, updateId, name, null);
            }
        } else if (type == 2) {
            // 微信
            String unionid = vo.getUnionid();
            //加锁防止连续点击
            String lockKey = "lock:submit:" + unionid;
            if (redisService.tryLock(lockKey, 5)) {
                try {
                    account = this.findOne(query.addCriteria(Criteria.where("unionid").is(unionid)));
                    if (account != null) {
                        Update update = new Update();
                        update.set("wx_name", name);
                        update.set("wx_status", 1);
                        update.set("login_type", "APP");
                        account.setWxName(name);
                        //需要检查是否订阅赛诺模板推送的参数,增加条记录 (Vipcns 在微信公众号那边进行关联)
                        account.setWx_status(1);
                        account.setLoginType("APP");
                        WxVipcns wxVipcns = wxVipCnsService.findOne(new Query().addCriteria(Criteria.where("unionid").is(vo.getUnionid())));
                        if (wxVipcns != null) {
                            String vipcn_openid = wxVipcns.getOpenid();
                            update.set("vipcn_openid", vipcn_openid);
                            update.set("subscribe", 1);
                            update.set("wechat_send_status", 1);
                            account.setVipcn_openid(vipcn_openid);
                            account.setSubscribe(1);
                            account.setWechat_send_status(1);
                        }
                        //this.save(account);
                        this.updateById(account.getId(), update);
                    } else {
                        //account = this.createAccount(type, updateId, name,null);
                       /* account.setUnionid(unionid);
                        account.setWxName(name);
                        account.setWx_status(1);
                        account.setLoginType("APP");*/
                        account = this.saveThirdAccount(type, updateId, name, null, unionid, name, 1, "APP");
                        //this.save(account);
                        vipMemberService.presentVipMember(account.getId(), (long) (7 * 24 * 60 * 60));
                    }
                } catch (Exception e) {
                    log.error("第三方微信登录出现异常，异常信息为：{}", e.getMessage());
                } finally {
                    redisService.del(lockKey);
                }
            }
        } else if (type == 3) {
            // imei
            String imei = vo.getImei();
            Device device = deviceService.findById(imei);
            if (device == null) {
                String failStr = MessageUtils.getLocale("API.request.deviceNotFind");
                return ResponseMap.fail(failStr);
            }
            String password = vo.getPassword();
            if (needDecryption) {
                password = DESEncryptTools.decode(password);
            }
            if (device.getUserMap() != null) {
                Map<String, Map<String, Object>> userMap = device.getUserMap();
                for (String key : userMap.keySet()) {
                    JSONObject item = new JSONObject(userMap.get(key));
                    if (item.containsKey("identity")) {
                        if ("admin".equals(item.getString("identity"))) {
                            // 如有已经有 且为 admin 那么提示 登录失败，设备已有管理员绑定，请使用账号登录 //API.request.login.imeiLoginFail
                            String failStr = MessageUtils.getLocale("API.request.login.imeiLoginFail");
                            return ResponseMap.fail(failStr);
                        }
                    }
                }
            }
            account = this.findOne(query.addCriteria(Criteria.where("imei").is(imei)));
            if (account != null) {
                aUpdate.set("wx_name", name);
            } else {
                log.info("imei:{} 登录新建 account", imei);
                //如果device没有token 去Redis里面取
                if (device.getToken() == null) {
                    String token = redisClusterService.hget(imei, "token");
                    String ttime = redisClusterService.hget(imei, "ttime");
                    Update tempUpdate = new Update();
                    tempUpdate.set("token", token);
                    tempUpdate.set("ttime", ttime);
                    deviceService.updateById(imei, tempUpdate);
                }
                //初次使用时用设备 imei 生成用户账号, 默认密码 123456
                UpdateVO accountUpdate = new UpdateVO();
                account = this.createAccount(type, imei, "用户" + RandomUtil.getRandomStr(6), null);
                accountUpdate.set("P", "123456");
                //群聊的创建与绑定
                ChatGroup chatGroup = chatGroupService.getChatGroupByDeviceG(device.getChatGroupMap(), account.getId());

                //用户绑定上设备 ==> "d" 字段 (deviceMap)
                AddRelatedDeviceReqBodyFormVO tempVo = new AddRelatedDeviceReqBodyFormVO();
                tempVo.setRelationship_image_id(9);
                tempVo.setRelationship("亲人");
                tempVo.setPhone("");
                addDeviceHandle(account, chatGroup.getId(), device, accountUpdate, tempVo);
                Map<String, Object> tempSub = account.getDeviceMap().get(imei);
                tempSub.put("identity", "visitor"); //修改标识是游客
                account.getDeviceMap().put(imei, tempSub);
                accountUpdate.setMap("d", account.getDeviceMap());
                //用户绑定上群组 ==> "g" 字段 (chatGroupMap)
                addChatGroupHandle(account, chatGroup.getId(), accountUpdate);

                //多人群组的绑定
                UpdateVO chatGroupUpdate = new UpdateVO();
                chatGroupService.addAccountHandle(chatGroup, account.getId(), chatGroupUpdate);
                chatGroupService.addDeviceHandle(chatGroup, device.getId(), device.getVendor(), chatGroupUpdate);

                //单聊群组创建和绑定
                singleChatGroupService.createSingleG(account.getId(), device.getId());

                UpdateVO deviceUpdate = new UpdateVO();
                //设备的绑定 把用户加到这个 device 的 u 中 (同时也是增加家庭成员)(绑定关系)
                deviceService.addAccountHandle(device, account, chatGroup.getId(), deviceUpdate, tempVo);
                deviceService.addChatGroupHandle(device, chatGroup.getId(), deviceUpdate);

                //更新操作
                chatGroupService.updateById(new ObjectId(chatGroup.getId()), chatGroupUpdate);

                //关键的保存设备的修改信息的数据库更新操作
                deviceService.updateById(device.getId(), deviceUpdate);

                this.updateById(account.getId(), accountUpdate);

            }
        } else if (type == 7) {
            // 苹果
            account = this.findOne(query.addCriteria(Criteria.where("appleid").is(vo.getId())));
            if (account == null) {
                account = this.createAccount(type, updateId, name, null);
                account.setLoginType("APP");
            } else {
                account.setAppleName(name);
            }
            this.save(account);
        } else if (type == 8) {
            // 小程序: 词典笔
            // vo.getId() 取的是小程序 用户 UnionID
            // https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
            if (StringUtils.isNotBlank(updateId)) {
                if (updateId.length() != 28 && updateId.length() != 32) {
                    return ResponseMap.fail("Parameter error");
                }
                account = this.findById("wxsp_" + updateId);
                if (account == null) {
                    account = this.createAccount(type, "wxsp_" + updateId, name, null);
                    account.setLoginType("wxsp");
                } else {
                    account.setName(name);
                }
                if (vo.getPhone() != null) {
                    account.setPhone(vo.getPhone());
                }
                this.save(account);
            } else {
                ResponseMap.fail("miss id");
            }
        }
        res.put("status", 0);
        if (account != null) {
            //单独微信一个账号（下），不需要和手机账号绑定在一起才可以使用（上）
            //判断手机绑定状态: 0:已绑定, 1:未绑定
            if (StringUtils.isNotBlank(account.getPhone())) {
                res.put("status", 1);
            }
            //返回正常的 openid 和 accesstoken
            res.put("openid", account.getId());
            String oldRegistrationID = account.getRegistrationID();
            if (registrationID != null) {
                aUpdate.set("registrationID", registrationID);
                aUpdate.set("oldRegistrationID", oldRegistrationID);
            }
            //更新登录时间
            aUpdate.set("login_date", new Date());

            this.updateById(account.getId(), aUpdate);
            //重新生成 accesstoken
            String OldAccesstoken = account.getAccesstoken();
            String accesstoken = updateAccesstoken(new Update(), account.getId(), OldAccesstoken, Constant.INVALID);
            res.put("accesstoken", accesstoken);
            loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), "微信", 1,
                    "登录", 1, vo.toString(), "AccountService.thirdlogin", "第三方用户登录成功");
        }
        //userKeyActionService.add(3,account.getId(), String.format("第三方 用户登录 type:%d", type));
        return ResponseMap.success(res);
    }


    /**
     * 第三方登录:判断手机号是否能绑定
     *
     * @param phone
     * @param bindCheckType
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap bindPhoneGet(String phone, Integer bindCheckType) {
        if (VerificationCodeUtil.isPhoneNum(phone)) {
            JSONObject res = new JSONObject();
            //手机号登录
            Account account = this.findOne(new Query().addCriteria(Criteria.where("phone").is(phone)));
            if (account != null) {
                String tempId = null;
                String tempdes = null;
                if (bindCheckType == 1) {
                    tempId = account.getQqid();
                    tempdes = "QQ";
                } else if (bindCheckType == 2) {
                    tempId = account.getWxid();
                    tempdes = "微信";
                } else if (bindCheckType == 7) {
                    tempId = account.getAppleid();
                    tempdes = "apple账号";
                }
                if (tempId != null) {
                    // 对应类型已有第三方Id
                    String failStr = MessageUtils.getLocale("API.request.register.bindFailDes1", tempdes);
                    return ResponseMap.fail(failStr);
                } else {
                    //已注册
                    res.put("registrationStatus", 1);
                }
            } else {
                //未注册
                res.put("registrationStatus", 0);
            }
            return ResponseMap.success(res);
        } else {
            return ResponseMap.fail("手机号不合规");
        }
    }

    /**
     * 第三方登录:绑定手机号
     *
     * @param vo
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap bindPhonePost(AccountsBindPhoneReqVO vo) {

        String phone = vo.getPhone();
        //验证验证码
        String upVCode = vo.getVCode();
        String vcode = redisClusterService.get(CacheEnum.VCODE.getKey() + phone);
        //校验验证码
        if (!upVCode.equals(vcode)) {
            return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
        } else {
            //验证成功后 验证码需要失效
            redisClusterService.del(CacheEnum.VCODE.getKey() + phone);
        }

        String bindId = vo.getBindId();
        Integer bindCheckType = vo.getBindCheckType();

        String password = vo.getPassword();
        Account account = this.findOne(new Query().addCriteria(Criteria.where("phone").is(phone)));
        if (account == null) {
            //未注册
            account = createAccount(bindCheckType, bindId, "", null);
            account.setPwd(password);
            account.setPhone(phone);
        } else {
            //已注册
            String tempId = null;
            String tempdes = null;
            if (bindCheckType == 1) {
                tempId = account.getQqid();
                tempdes = "QQ";
            } else if (bindCheckType == 2) {
                tempId = account.getWxid();
                tempdes = "微信";
            } else if (bindCheckType == 7) {
                tempId = account.getAppleid();
                tempdes = "apple账号";
            }
            if (tempId != null) {
                // 对应类型已有第三方Id
                String failStr = MessageUtils.getLocale("API.request.register.bindFailDes1", tempdes);
                return ResponseMap.fail(failStr);
            }
            account.setPhone(phone);
        }
        if (bindCheckType == 1) {
            account.setQqid(bindId);
            account.setQqName(vo.getName());
        } else if (bindCheckType == 2) {
            account.setWxid(bindId);
            account.setWxName(vo.getName());
        } else if (bindCheckType == 7) {
            account.setAppleid(bindId);
            account.setAppleName(vo.getName());
        }
        //更新登录时间
        account.setLoginDate(new Date());
        this.save(account);
        //重新生成 accesstoken
        String OldAccesstoken = account.getAccesstoken();
        String accesstoken = updateAccesstoken(new Update(), account.getId(), OldAccesstoken, Constant.INVALID);
        JSONObject res = new JSONObject();
        res.put("openid", account.getId());
        res.put("accesstoken", accesstoken);

        return ResponseMap.success(res);
    }

    /**
     * 用户绑定第三方账号
     *
     * @param vo
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap bindOtherAccount(AccountsBindOtherReqVO vo) {

        Account account = this.findById(vo.getOpenid());
        if (account != null) {
            String name = vo.getName();
            Integer bindCheckType = vo.getBindCheckType();
            String bindId = vo.getBindId();
//            String token = vo.getToken();
            //如绑定的第三方账号 在系统中已有绑定的用户(手机号),返回错误提示
            long count = 0;
            String tempdes = "";
            UpdateVO updateVO = new UpdateVO();
            if (bindCheckType == 1) {
                count = this.count(new Query().addCriteria(Criteria.where("qqid").is(bindId)));
                tempdes = "QQ";
                updateVO.set("qqid", bindId);
                updateVO.set("qqName", name);
            } else if (bindCheckType == 2) {
                count = this.count(new Query().addCriteria(Criteria.where("wxid").is(bindId)));
                tempdes = "微信";
                updateVO.set("wxid", bindId);
                updateVO.set("wxName", name);
            } else if (bindCheckType == 7) {
                count = this.count(new Query().addCriteria(Criteria.where("appleid").is(bindId)));
                tempdes = "apple账号";
                updateVO.set("appleid", bindId);
                updateVO.set("appleName", name);
            }
            if (count > 0) {
                return ResponseMap.fail(MessageUtils.getLocale("API.request.register.bindFailDes2", tempdes));
            }
            this.updateById(vo.getOpenid(), updateVO);


        } else {
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }

        return ResponseMap.success();
    }

    /**
     * 用户解绑第三方账号
     *
     * @param vo
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap unbindOtherAccount(AccountsUnBindOtherReqVO vo) {

        Account account = this.findById(vo.getOpenid());
        if (account != null) {
            Integer bindCheckType = vo.getBindCheckType();
            UpdateVO updateVO = new UpdateVO();

            if (bindCheckType == 1) {
                updateVO.unset("qqid");
                updateVO.unset("qqName");
            } else if (bindCheckType == 2) {
                updateVO.unset("wxid");
                updateVO.unset("wxName");
            } else if (bindCheckType == 7) {
                updateVO.unset("appleid");
                updateVO.unset("appleName");
            }
            this.updateById(vo.getOpenid(), updateVO);
        } else {
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }

        return ResponseMap.success();
    }


    /**
     * @param account
     * @time: 2020/11/26 10:25
     * @author: Rock
     * @version 1.0
     */
    public String getAccesstoken(Account account) {
        String accesstoken = null;
        if (account != null) {
            accesstoken = account.getAccesstoken();
            Double accesstokenTime = account.getAccesstokenTime();
            boolean reset = false;
            if (StringUtil.isBlank(accesstoken)) {
                if (!account.getId().equals(redisClusterService.get(CacheEnum.UA.getKey() + accesstoken))) {
                    //不相等, 重置accesstoken
                    reset = true;
                }
            }

            //accesstoken 不存在或超过 6 天未更新则重新生成accesstoken
            if (StringUtil.isBlank(accesstoken) || accesstokenTime == null
                    || DateUtil.nowTimestampToLong() - accesstokenTime > 518400) {
                //log.info("accesstoken={}. accesstokenTime={}. DateUtil.nowTimestampToDouble()={}", accesstoken, accesstokenTime, DateUtil.nowTimestampToDouble());
                reset = true;
            }
            if (reset) {
                //重新生成
                accesstoken = updateAccesstoken(new Update(), account.getId(), null, null);
            }
        }
        return accesstoken;
    }

    /**
     * 更新用户登录accesstoken
     *
     * @param update         要更新到mongo的对象属性
     * @param id             openid
     * @param oldAccesstoken 需要设置 的旧的token
     * @param invalidValue   设置的token无效值
     * @time: 2021-10-25 14:00
     * @author: Weichao
     * @version 1.0
     */
    public String updateAccesstoken(Update update, String id, String oldAccesstoken, String invalidValue) {
        if (Constant.PWDCHANGE.equals(invalidValue) && StringUtil.isNotBlank(oldAccesstoken)) {
            setAccessTokenCache(oldAccesstoken, Constant.PWDCHANGE);
        } else if (Constant.INVALID.equals(invalidValue) && StringUtil.isNotBlank(oldAccesstoken)) {
            setAccessTokenCache(oldAccesstoken, Constant.INVALID);
        }

        //生成12位长度的accesstoken
        String accesstoken = RandomUtil.getRandomStr(12);
        //设置accesstoken缓存
        setAccessTokenCache(accesstoken, id);
        //更新账号的accesstoken记录
        update.set("a", accesstoken);
        update.set("at", DateUtil.nowTimestampToLong());
        log.info("id = {}.   update = {} ", id, update.toString());
        this.updateById(id, update);
        return accesstoken;
    }

    /**
     * @param account
     * @param chatGroupId
     * @time: 2020/11/4 16:01
     * @author: Rock
     * @version 1.0
     */
    public void addChatGroup(Account account, String chatGroupId) {
        if (account.getChatGroupMap() == null) {
            account.setChatGroupMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", DateUtil.nowTimestampToLong());
        account.getChatGroupMap().put(chatGroupId, sub);
        Update update = new Update();
        update.set("g", account.getChatGroupMap());
        this.updateById(account.getId(), update);
    }

    /**
     * @param account
     * @param device
     * @param chatGroupId
     * @time: 2020/11/4 15:54
     * @author: Rock
     * @version 1.0
     */
    public void addDevice(Account account, Device device, String chatGroupId) {
        if (account.getDeviceMap() == null) {
            account.setDeviceMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", DateUtil.nowTimestampToLong());
        sub.put("v", device.getVendor());
        sub.put("g", chatGroupId);
        account.getDeviceMap().put(device.getId(), sub);
        Update update = new Update();
        update.set("d", account.getDeviceMap());
        this.updateById(account.getId(), update);
    }

    /**
     * @param account
     * @time: 2020/11/4 17:03
     * @author: Rock
     * @version 1.0
     */
    public void del(Account account) {
        if (StringUtil.isNotBlank(account.getAccesstoken())) {
            RedisClusterService.getJedisCluster().del(account.getAccesstoken());
        }
        log.info("account deleted= {}", account.getId());
        this.delById(account.getId());
    }

    /**
     * 忘记密码
     *
     * @param vo
     * @param needDecryption 是否需要加密
     * @time: 2021-10-20 18:45
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap resetPassword(ResetPasswordReqBodyFormVo vo, boolean needDecryption) {
        String password = vo.getPassword();
        String phone = vo.getPhone();
        String postfixKey = "";
        //检查参数有没有传
        if (StringUtils.isAnyBlank(phone, vo.getAuth_code())) {
            log.error("参数错误，未传手机号或国家编码");
            return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), ResponseEnum.STATUS400.getMsg());
        }
        if (needDecryption) {
            phone = DESEncryptTools.decode(phone);
            postfixKey = phone;
        }

        if (!ObjectUtils.isEmpty(vo.getSendCodeWay()) && Integer.valueOf(1).equals(vo.getSendCodeWay())) {
            if (StringUtils.isEmpty(vo.getEmail())) {
                return ResponseMap.fail("参数缺失，用Email方式发送的验证码，却没传email值，修改用户信息失败");
            }
            postfixKey = vo.getEmail();
        }

        String codeKey = Constant.SMS_KEY_PREFIX + vo.getUuid() + postfixKey;
        //检查验证码
        ResponseMap status400 = checkVerifyCode(vo.getAuth_code(), codeKey);
        if (status400 != null) return status400;

        Account account = this.findOne(new Query(Criteria.where("phone").is(phone).and("country_code").is(vo.getCountry_code())));
        if (account == null) {
            log.error("用户不存在，请先注册，系统不存在的用户号码为：{}", vo.getPhone());
            //该账号未注册 API.request.register.notAccount
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.register.notAccount");
        }
        /*String imei = vo.getImei();
        if(imei != null){
            Device device = deviceService.findById(imei);
            if(device == null){
                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
            }
            Map<String, Map<String, Object>> deviceMap = account.getDeviceMap();
            if(deviceMap == null || deviceMap.size() == 0){
                //您输入的手机号未绑定设备（%s），无法重置密码 API.request.account.notDevice
                return ResponseMap.fail(ResponseEnum.STATUS405.getCode(), "API.request.account.notDevice", imei);
            }
            if(!deviceMap.keySet().contains(imei)){
                return ResponseMap.fail(ResponseEnum.STATUS405.getCode(), "API.request.account.notDevice", imei);
            }
        }*/

        Update update = new Update();
        update.set("P", password);
        updateAccesstoken(update, account.getId(), account.getAccesstoken(), Constant.PWDCHANGE);
        log.info("重置账号密码成功 vo:{}", vo);
        //userKeyActionService.add(4, account.getId(), "重置账号密码");
        //记录日志
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), "手机号", 4,
                "重置用户密码", 1, vo.toString(), "AccountService.resetPassword", "重置成功");
        return ResponseMap.success();
    }


    /**
     * 获取账号用户信息
     *
     * @param openid
     * @time: 2020/11/6 15:04
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getInfo(String openid) {
        Account account = this.findById(openid);
        if (account != null) {
            ResponseMap res = ResponseMap.success();
            //是否开启了微信公众号消息(1,0, 2:未关注)
            int status = 2; //未关注
            if (account.getSubscribe() == 1) {//是否订阅
                status = account.getWechat_send_status();
            }
            res.put("status", status);
            if (account.getCountryCode() != null) {
                res.put("account_status", 1); // 手机
            } else {
                res.put("account_status", 0);
            }
            res.put("email", account.getEmail() == null ? "" : account.getEmail());
            res.put("phone", account.getPhone() == null ? "" : account.getPhone());
            res.put("phone_caring", account.getPhoneM() == null ? "" : account.getPhoneM());
            res.put("name", account.getName() == null ? "" : account.getName());
            res.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH
                    + (StringUtil.isBlank(account.getImage()) ? Const.DEFAULT_JPG : account.getImage()));
            res.put("letMeAlone", account.getLetMeAlone() == null ? false : account.getLetMeAlone());
            res.put("accesstoken", account.getAccesstoken());
            res.put("juphoonKey", jfAppKey);
            //读取一个 {} 有对应的包名的才进行增加属性
            JSONObject feedbackEmailMap = JSONObject.parseObject(feedbackEmailMapStr);

            if (feedbackEmailMap.containsKey(account.getPackageName())) {
                res.put("feedbackEmail", feedbackEmailMap.getString(account.getPackageName()));
            } else {
                //配置存在的话, 默认用赛诺的, 否则不返回, 用APP自带的 邮箱配置
                if (feedbackEmailMap.containsKey("com.jianyou.tongxin")) {
                    res.put("feedbackEmail", feedbackEmailMap.getString("com.jianyou.tongxin"));
                }
            }

            res.put("wx_status", 0);
            if (account.getWxName() != null) {
                //微信登录
                if (account.getImage() != null && account.getImage().contains("http")) {
                    res.put("avator", account.getImage());
                }
                res.put("wx_name", account.getWxName());
                res.put("wx_status", 1);
            }
            if (account.getQqName() != null) {
                res.put("qqName", account.getQqName());
            }
            if (account.getAppleName() != null) {
                res.put("appleName", account.getAppleName());
            }
            //重置缓存时间
            setAccessTokenCache(account.getAccesstoken(), openid);
            Update update = new Update();
            update.set("at", DateUtil.nowTimestampToLong());
            //更新时间
            this.updateById(account.getId(), update);
            return res;
        }
        return ResponseMap.fail();
    }

    /**
     * 更新账号信息
     *
     * @param vo
     * @param needDecryption 是否需要加密
     * @time: 2020/11/6 15:52
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap updateInfo(AccountUnpdateReqBodyFormVO vo, MultipartFile image, boolean needDecryption) throws IOException {
        String openid = vo.getOpenid();
        Account account = this.findById(openid);
        if (account == null) {
            log.error("Account is null. id={}. updateInfo vo={}", openid, vo);
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }
        ResponseMap res = ResponseMap.success();
        Update update = new Update();
        if (StringUtil.isNotBlank(vo.getName())) {
            update.set("name", vo.getName());
            //检查 用户昵称
            if (!ContentSecurityUtil.nicknameAudit(vo.getName())) {
                //昵称包含敏感词汇
                userKeyActionService.add(10, vo.getOpenid(), "用户:" + vo.getOpenid() + " 昵称包含敏感词汇:" + vo.getName());
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.requset.connot.submit.word");
            }
        }
        if (StringUtil.isNotBlank(vo.getQqid())) {
            update.set("qqid", vo.getQqid());
            RedisClusterService.getJedisCluster().hset(account.getImei(), "qqid", "1");
        }
        if (StringUtil.isNotBlank(vo.getWxid())) {
            update.set("wxid", vo.getWxid());
            RedisClusterService.getJedisCluster().hset(account.getImei(), "wxid", "1");
        }
        if (vo.getLetMeAlone() != null && vo.getLetMeAlone()) {
            update.set("letMeAlone", true);
        }
        if (image != null && !image.isEmpty()) {
            String oldImageName = account.getImage();
            if (StringUtil.isNotBlank(oldImageName) && !oldImageName.contains("default.jpg")) {
                //删除旧文件
                baseBucketTool.delFile(BaseBucketTool.ACCOUNT_COMMON_PATH + oldImageName);
            }
            //生成文件名并上传
            String fileName = BaseBucketTool.createFileNameToJpg();
            baseBucketTool.upload(BaseBucketTool.ACCOUNT_COMMON_PATH + fileName, image, 1);

            //检查 设备头像
            if (!ContentSecurityUtil.imageAudit(baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH + fileName, openid)) {
                //头像包含敏感头像
                //删除敏感头像
//                baseBucketTool.delFile(BaseBucketTool.ACCOUNT_COMMON_PATH + fileName);
                userKeyActionService.add(10, vo.getOpenid(), "用户:" + vo.getOpenid() + " 修改头像,违规:" + baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH + fileName);
                return ResponseMap.fail(ResponseEnum.STATUS400.getCode(), "API.requset.connot.upload.avatar");
            }
            update.set("image", fileName);
            //更新用户所拥有设备新版微聊协议对应的头像信息，发送更新微聊图像的指令
            wechatContactService.updateUserWechatContact(account.getDeviceMap(), account.getId(), BaseBucketTool.ACCOUNT_COMMON_PATH + fileName);
            res.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.ACCOUNT_COMMON_PATH + fileName);
        }
        String password = vo.getPassword();
        String newPassword = vo.getNewPassword();
        String phone = vo.getPhone();

        if (password != null) {
            if (needDecryption) {
                password = DESEncryptTools.decode(password);
                newPassword = DESEncryptTools.decode(newPassword);
                if (password == null) {
                    return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
                }
                if (StringUtil.isNotBlank(phone)) {
                    phone = DESEncryptTools.decode(phone);
                    update.set("phone", phone);
                    //userKeyActionService.add(5,openid, "修改用户资料时 修改手机号");
                }
                password = DESEncryptTools.encode(password);
                newPassword = DESEncryptTools.encode(newPassword);

            }
            //登录状态修改密码
            if (!password.equals(account.getPwd())) {
                return ResponseMap.fail(ResponseEnum.STATUS5009.getCode(), ResponseEnum.STATUS5009.getMsg());
            }

            update.set("P", newPassword);
            updateAccesstoken(update, account.getId(), account.getAccesstoken(), Constant.PWDCHANGE);
            //userKeyActionService.add(4,account.getId(), "修改用户资料时修改密码");
            //记录日志
            loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), "", 5,
                    "修改用户信息", 1, vo.toString(), "AccountService.updateInfo", "修改成功");
            return res;
        }
        if (StringUtil.isNotBlank(phone)) {
            if (needDecryption) {
                phone = DESEncryptTools.decode(phone);
                if (phone == null) {
                    return ResponseMap.fail(ResponseEnum.STATUS7000.getCode(), ResponseEnum.STATUS7000.getMsg());
                }
            }
            update.set("phone", phone);
            //userKeyActionService.add(5,openid, "修改用户资料时 修改手机号");
        }
        this.updateById(account.getId(), update);
        //记录日志
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), "", 5,
                "修改用户信息", 1, vo.toString(), "AccountService.updateInfo", "修改成功");
        return res;
    }


    /**
     * 密码校验
     *
     * @param paramsMap
     * @time: 2021-10-21 10:20
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap passwordAuth(AccountPasswordReqVO paramsMap) {
        String accountStr = paramsMap.getAccount();
        String qStr = "email";
        Integer type = paramsMap.getType();
        if (type == 0) {
            qStr = "phone";
        }
        String pwd = paramsMap.getPassword();
        Account account = this.findOne(new Query(Criteria.where(qStr).is(accountStr)));

        if (!account.getPwd().equals(pwd)) {
            return ResponseMap.fail(ResponseEnum.STATUS5009.getCode(), ResponseEnum.STATUS5009.getMsg());
        }
        //生成临时凭证, 用于修改更换手机号和注销账号:
        String t_token = RandomUtil.getRandomStr(12);
        redisClusterService.setex(CacheEnum.T_TOKEN.getKey() + accountStr, CacheEnum.T_TOKEN.getExpire(), t_token);
        ResponseMap res = ResponseMap.success();
        res.put("t_token", t_token);

        return res;

    }


    /**
     * 手机号/邮箱校验
     *
     * @param paramsMap
     * @time: 2022-2-15 10:20
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap phoneOrEmailAuth(AccountPhoneOrEmailReqVO paramsMap) {
        String upVCode = paramsMap.getVCode();
        Integer type = paramsMap.getType();
        String tempIdent = paramsMap.getPhone();
        if (type == 1) {
            tempIdent = paramsMap.getEmail();
        }
        String vcode = redisClusterService.get(CacheEnum.VCODE.getKey() + tempIdent);
        //校验验证码
        if (!upVCode.equals(vcode)) {
            return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
        } else {
            //验证成功后 验证码需要失效
            redisClusterService.del(CacheEnum.VCODE.getKey() + tempIdent);
        }
        //生成临时凭证, 用于修改更换手机号和注销账号:
        String t_token = RandomUtil.getRandomStr(12);
        redisClusterService.setex(CacheEnum.T_TOKEN.getKey() + tempIdent, CacheEnum.T_TOKEN.getExpire(), t_token);
        ResponseMap res = ResponseMap.success();
        res.put("t_token", t_token);

        return res;
    }

    /**
     * 账号是否存在的校验
     *
     * @param account 账号
     * @param type    账号类型 : 0:手机号 1:邮箱
     * @time: 2021-10-21 10:20
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap accountExists(String account, Integer type) {
        if (StringUtils.isBlank(account)) {
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }
        String qStr = "email";
        if (type == 0) {
            qStr = "phone";
            //检查账号是否存在
            long count = this.count(new Query(Criteria.where(qStr).is(account)));
            if (count == 0) {
                return ResponseMap.fail(ResponseEnum.STATUS5011.getCode(), ResponseEnum.STATUS5011.getMsg());
            }
        }
        //检查账号是否存在
        long count = this.count(new Query(Criteria.where(qStr).is(account)));
        if (count == 0) {
            return ResponseMap.fail(ResponseEnum.STATUS5013.getCode(), ResponseEnum.STATUS5013.getMsg());
        }


        return ResponseMap.success();
    }


    /**
     * 更换账号登录标识 (更换手机号/更换邮箱)
     *
     * @param
     * @time: 2022-02-12 09:51
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap changeAccountLoginIdent(AccountChangeLoginIdentReqVO vo) {
        String oldLoginIdent = vo.getOldLoginIdent();
        String loginIdent = vo.getLoginIdent();
        Integer type = vo.getType();

        String upT_token = vo.getT_token();
        // oldPhone 的授权码认证
        String t_token = redisClusterService.get(CacheEnum.T_TOKEN.getKey() + oldLoginIdent);
        if (t_token == null || !t_token.equals(upT_token)) {
            return ResponseMap.fail(ResponseEnum.STATUS5014.getCode(), ResponseEnum.STATUS5014.getMsg());
        }

        String qStr = "email";
        if (type == 0) {
            qStr = "phone";
        }

        //检查新的登录标识是否已存在
        long count = this.count(new Query(Criteria.where(qStr).is(loginIdent)));
        if (count > 0) {
            return ResponseMap.fail(ResponseEnum.STATUS5010.getCode(), ResponseEnum.STATUS5010.getMsg());
        }

        String upVCode = vo.getVCode();
        String vcode = redisClusterService.get(CacheEnum.VCODE.getKey() + loginIdent);
        //校验验证码
        if (!upVCode.equals(vcode)) {
            return ResponseMap.fail(ResponseEnum.STATUS5007.getCode(), ResponseEnum.STATUS5007.getMsg());
        } else {
            //验证成功后 验证码需要失效
            redisClusterService.del(CacheEnum.VCODE.getKey() + loginIdent);
        }

        //认证成功,授权码移除
        redisClusterService.del(CacheEnum.T_TOKEN.getKey() + oldLoginIdent);


        Account account = this.findById(vo.getOpenid());
        if (account != null) {
            if (type == 0) {
                //更换手机号
                //已绑定多个设备，更换手机号码，更换成功，需同步修改已绑定设备中的家庭成员的号码。
                for (String itemImei : account.getDeviceMap().keySet()) {
                    DeviceUpdateMemberReqVO updateMemberVO = new DeviceUpdateMemberReqVO();
                    updateMemberVO.setId(account.getId());
                    updateMemberVO.setImei(itemImei);
                    updateMemberVO.setPhone(loginIdent);
                    deviceService.updateMember(updateMemberVO);
                }
            }
            Update update = new Update();
            update.set(qStr, loginIdent);
            //更换后应更新 accesstoken, 使其重新登录
            updateAccesstoken(update, account.getId(), account.getAccesstoken(), Constant.INVALID);
        } else {
            log.error("找不到的openid={} phone: {} 的账号", vo.getOpenid(), loginIdent);
            return ResponseMap.fail();
        }

        return ResponseMap.success();

    }

    /**
     * 设置token缓存
     *
     * @param accesstoken
     * @param value
     * @time: 2020/11/6 15:29
     * @author: Rock
     * @version 1.0
     */
    private void setAccessTokenCache(String accesstoken, String value) {
        String activeProfile = SpringContextUtil.getActiveProfile();
        if ("dev".contains(activeProfile)) {
//            log.info("测试环境token失效时间为1天");
//            redisClusterService.setex(CacheEnum.UA.getKey() + accesstoken, 3600*24*1, value);// 1天
            log.info("测试环境token失效时间为4小时");
            redisClusterService.setex(CacheEnum.UA.getKey() + accesstoken, 60 * 60 * 4, value);// 4小时
            return;
        }
        redisClusterService.setex(CacheEnum.UA.getKey() + accesstoken, CacheEnum.UA.getExpire(), value);
    }

    /**
     * 清除token缓存
     *
     * @param accesstoken
     * @time: 2021-10-21 10:59
     * @author: Weichao
     * @version 1.0
     */
    private void delAccessTokenCache(String accesstoken) {
        redisClusterService.del(CacheEnum.UA.getKey() + accesstoken);
    }

    /**
     * 注销账号
     *
     * @param openid
     * @time: 2020/11/9 17:06
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap del(String openid) {
        Account account = findById(openid);
        if (account == null) {
            log.error("Account is null. id={}. del", openid);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }

        if (CollectionUtil.isNotEmpty(account.getDeviceMap())) {
            //由于注销账号的接口注定是很少使用的 ,所以查询操作多点也没什么问题
            //先监测有无挂失的设备
            for (String imei : account.getDeviceMap().keySet()) {
                Device dev = deviceService.findById(imei);
                if (dev.getLossDevStatus() == 1) {
                    //"账号下的设备处于挂失状态，无法注销账号"
                    return ResponseMap.fail(ResponseEnum.STATUS6001.getCode(), "API.request.deviceLossingNotClearAccount");
                }
            }

            //解绑账号下的所以设备
            for (String imei : account.getDeviceMap().keySet()) {
                /*ImeiParamReqBodyFormVO reqVO = new ImeiParamReqBodyFormVO();
                reqVO.setImei(imei);
                reqVO.setOpenid(account.getId());
                reqVO.setAccesstoken(account.getAccesstoken());
                //解绑设备操作
                this.delRelatedDevice(reqVO);*/
                log.info("注销用户时，解绑设备：{}，用户绑定设备信息为：{}", imei, account.getDeviceMap());
                unBindDeviceBeforeCancelUser(account, imei, account.getDeviceMap().get(imei).get("identity").toString());
            }
        }
        //删除账号token
        if (StringUtil.isNotBlank(account.getAccesstoken())) {
            RedisClusterService.getJedisCluster().del(account.getAccesstoken());
        }
        //清除账号图片
        if (StringUtil.isNotBlank(account.getImage()) && !account.getImage().contains("default.jpg")) {
            //删除旧文件
            baseBucketTool.delFile(BaseBucketTool.ACCOUNT_COMMON_PATH + account.getImage());
        }
        log.info("Account deleted: id={}", account.getId());
        this.delById(account.getId());
        //userKeyActionService.add(2,openid, "用户手动注销");
        //记录日志
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), null, 3,
                "注销用户", 1, openid, "AccountService.del", "注销成功");
        return ResponseMap.success();
    }


    /**
     * @param [account, imei, identity]
     * @return void
     * @description 解绑设备
     * @author cwj
     * @date @time 2025/6/2 17:43
     **/
    public void unBindDeviceBeforeCancelUser(Account account, String imei, String identity) {
        try {
            Device device = deviceService.findById(imei);
            if (device == null) {
                log.info("Device is null. params={}", imei);
                operateLogService.saveOperateLog(account.getId(), account.getName(), imei, "设备解绑", 0, "",
                        "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备失败，系统中不存在设备" + imei);
                return;
            }
            //构建 删除家庭成员的 请求体
            DeviceDelMemberReqVO delMemberReqVO = new DeviceDelMemberReqVO();
            delMemberReqVO.setId(account.getId());
            delMemberReqVO.setOpenid(account.getId());
            delMemberReqVO.setImei(imei);
            //删除成员并推送kafka消息
            deviceService.delMember(delMemberReqVO);
            if ("admin".equals(identity)) {
                log.info("imei:{} 的管理员:{} 解绑所有的用户", device.getId(), account.getId());
                deviceService.reset(device, account.getId(), "app");
                //userKeyActionService.add(8, account.getId(), String.format("imei:%s 的管理员:%s 解绑所有的用户",device.getId(), account.getId()));
                operateLogService.saveOperateLog(account.getId(), account.getName(), imei, "设备解绑", 1, "",
                        "AccountService.delRelatedDevice", String.format("imei:%s 的管理员:%s 解绑所有的用户成功", device.getId(), account.getId()));
            } else {
                //userKeyActionService.add(7,account.getId(), "解绑设备"+device.getId());
                operateLogService.saveOperateLog(account.getId(), account.getName(), imei, "设备解绑", 1, "",
                        "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备" + imei + "成功");
            }
        } catch (Exception e) {
            log.error("异常信息为：{}", e.getMessage());
        }

    }

    /**
     * 微信绑定
     *
     * @param vo
     * @time: 2/26/22 5:04 PM
     * @author: Weichao
     * @version 1.0
     */

    public ResponseMap wxBind(WxBindReqVO vo) {
        Account account = this.findById(vo.getOpenid());
        String wxId = vo.getId();
        if (this.count(new Query().addCriteria(Criteria.where("wxid").is(wxId))) > 0) {
            //此微信账号已经存在，不能申请绑定 API.request.wxAccountIsAlready
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.wxAccountIsAlready");
        }
        String unionid = vo.getUnionid();
        if (unionid != null && this.count(new Query().addCriteria(Criteria.where("unionid").is(unionid))) > 0) {
            //此微信账号已经存在，不能申请绑定 API.request.wxAccountIsAlready
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.wxAccountIsAlready");
        }
        account.setWxid(wxId);
        account.setWxName(vo.getName());
        if (vo.getUnionid() != null) {
            account.setUnionid(vo.getUnionid());
            WxVipcns wxVipcns = wxVipCnsService.findOne(new Query().addCriteria(Criteria.where("unionid").is(vo.getUnionid())));
            if (wxVipcns != null) {
                account.setVipcn_openid(wxVipcns.getOpenid());
                account.setSubscribe(1);
                account.setWechat_send_status(1);
            }
        }
        this.save(account);
        log.info("微信绑定成功 vo:{}", vo.toString());
        return ResponseMap.success();
    }

    /**
     * 微信解绑
     *
     * @param vo
     * @time: 2/26/22 5:04 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap wxUnbind(BaseAccountReqVO vo) {
        Account account = this.findById(vo.getOpenid());
        account.setWxid(null);
        account.setWxName(null);
        account.setUnionid(null);
        if (account.getVipcn_openid() != null) {
            account.setVipcn_openid(null);
        }
        account.setSubscribe(0);
        account.setWechat_send_status(0);
        this.save(account);
        log.info("微信解绑成功 vo:{}", vo.toString());
        return ResponseMap.success();
    }

    /**
     * 微信登录后申请绑定手机号码
     *
     * @param vo
     * @time: 2/26/22 4:44 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap apply_bind_phone(PhoneParamReqVO vo) {
        log.info("微信登录后申请绑定电话号码的参数信息：{}", vo);
        String phone = vo.getPhone();
        if (phone.length() == 11 && StringUtil.isNumeric(phone)) {
            ResponseMap responseMap = this.bindPhoneGet(phone, 2);
            if ("0".equals(responseMap.get("code").toString())) {
                if (responseMap.containsKey("registrationStatus") && "1".equals(responseMap.get("registrationStatus").toString())) {
                    //手机号已被注册了
                    return ResponseMap.fail("手机号已被注册");
                }
            } else {
                return responseMap;
            }
            Account account = this.findById(vo.getOpenid());
            account.setPhone(vo.getPhone());
            account.setCountryCode(ObjectUtils.isEmpty(vo.getCountry_code()) ? Constant.DEFAULT_COUNTRY_CODE : vo.getCountry_code());
            this.save(account);
            ResponseMap res = ResponseMap.success();
            res.put("phone", vo.getPhone());
            return res;
        } else {
            //请输入正确的手机号码 API.request.phoneIsNotCompliant
            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.phoneIsNotCompliant");
        }
    }

    /**
     * 扫码之后调用的接口
     *
     * @param vo
     * @time: 2/26/22 2:17 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap after(ImeiParamReqVO vo) {
        JSONObject res = new JSONObject();
        String imei = vo.getImei();
        Account account = this.findById(vo.getOpenid());
        int binded_status = 0;
        if (account != null) {
            //log.info("imei:{},绑定设备前的检查-1",vo.getImei());
            Map<String, Map<String, Object>> deviceMap = account.getDeviceMap();
            if (deviceMap != null) {
                // 限制用户添加设备的上限 6个
                // 如果是百合,不要限制
                if (!"bihee".equals(SpringContextUtil.packageName) && deviceMap.size() > 6 - 1) {
                    //您已有6个设备，不能添加其他设备
                    return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceTooMany");
                }
                for (String key : deviceMap.keySet()) {
                    JSONObject tempItem = new JSONObject(deviceMap.get(key));
                    if (key.equals(imei)) {
                        //log.info("imei:{},绑定设备前的检查0",vo.getImei());
                        //您已经绑定了这个手表了 API.request.deviceRepeatBinding
                        return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceRepeatBinding");
                    }
                    if ("visitor".equals(tempItem.getString("identity"))) {
                        //log.info("imei:{},绑定设备前的检查1",vo.getImei());
                        //imei账号没有权限添加关联设备，请注册手机账号再添加 API.request.accountNotPermissions
                        return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.accountNotPermissions");
                    }
                }
            }
            //log.info("imei:{},绑定设备前的检查2",vo.getImei());
            Device device = deviceService.findById(imei);
            //log.info("imei:{},绑定设备前的检查3",vo.getImei());
            if (device == null) {
                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
            }
//            String token = RedisClusterService.getJedisCluster().hget(vo.getImei(), ConstCacheField.TOKEN);
//            if (StringUtil.isBlank(device.getToken())) {
//                //判断设备是否联网激活 (有无token)
//                return ResponseMap.fail(ResponseEnum.STATUS600.getCode(), ResponseEnum.STATUS600.getMsg());
//            }
            //log.info("imei:{},绑定设备前的检查4",vo.getImei());
            Map<String, Map<String, Object>> userMap = device.getUserMap();
            int userMapNum = 0;
            if (userMap != null) {
                userMapNum = userMap.size();
                //如果判断绑定这个表的用户是有管理员的
                for (String key : userMap.keySet()) {
                    JSONObject tempItem = new JSONObject(userMap.get(key));
                    if ("admin".equals(tempItem.getString("identity"))) {
                        binded_status = 1;
                        Account adminAccount = this.findById(key);
                        if (ObjectUtils.isEmpty(adminAccount)) {
                            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceHasBindProblem");
                        }
                        res.put("admin_openid", key);
                        res.put("admin_phone", adminAccount.getPhone());
                    }
                }
            }
            //log.info("imei:{},绑定设备前的检查5",vo.getImei());
            //TODO 后续再需改进
//            if(device.getContactMap() != null && device.getContactMap().size() + userMapNum > 50-1){
            if (device.getContactMap() != null && device.getContactMap().size() > 50 - 1) {
                //log.info("imei:{},绑定设备前的检查7",vo.getImei());
                //通讯录号码不能超过50个 API.request.contactTooMany
                return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.contactTooMany");
            }
        } else {
            //log.info("imei:{},绑定设备前的检查8",vo.getImei());
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }
        res.put("binded_status", binded_status);
        return ResponseMap.success(res);
    }


    /**
     * 扫码后提交申请绑定
     * 如果扫码设备后, 后台返回设备有管理员,那么得使用此接口进行的 <申请绑定>
     *
     * @param vo
     * @time: 2/26/22 3:31 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap apply_bind(ApplyBindDeviceReqVO vo) {
        String imei = vo.getImei();
        Device device = deviceService.findById(imei);
//        //TODO 查询数据库 是否有重复提交的申请记录
//        if(deviceAttentionService.findRepeatRecord(imei, vo.getOpenid(), vo.getAdmin_openid()) > 0){
//            //如有,返回提示
//            //你已经申请绑定了，请耐心等待手表管理员审核 API.request.attentionApplyRepeat
//            return ResponseMap.fail(ResponseEnum.STATUS403.getCode(),"API.request.attentionApplyRepeat");
//        }
        //如无,那么生成一条记录
        Account account = this.findById(vo.getOpenid());
        if (account.getPhone() == null || account.getPhone().isEmpty()) {
            if (vo.getMy_phone() == null) {
                log.error("imei:{} 需要添加手机号后才能绑定设备 vo:{}", imei, vo);
                return ResponseMap.fail("需要添加手机号后才能绑定设备");
            } else {
                //更新用户的手机号
                this.updateById(vo.getOpenid(), new Update().set("phone", vo.getMy_phone()));
                account.setPhone(vo.getMy_phone());
                userKeyActionService.add(5, vo.getOpenid(), "扫码后提交申请绑定时 更新手机号");
            }
        }
        DeviceAttention deviceAttention = new DeviceAttention();
        deviceAttention.setImei(imei);
        deviceAttention.setOpenid(vo.getAdmin_openid());
        deviceAttention.setApply_openid(vo.getOpenid());
        String deviceName = deviceService.getDeviceName(device);
        deviceAttention.setName(deviceName);
        deviceAttention.setType("applicationRrecord");
        deviceAttention.setPhone(account.getPhone());
        deviceAttention.setRelationship(vo.getRelationship());
        deviceAttention.setRelationship_image_id(vo.getRelationship_image_id());
        // 用户(%s)申请关注【%s】的手表  API.request.attentionApply
        String msgTitle = MessageUtils.getLocale("API.request.attentionApply", account.getPhone(), deviceName);

        deviceAttention.setMsgTitle(msgTitle);
        deviceAttention.setMsgContent(msgTitle);
        String attention_id = deviceAttentionService.createNewAttention(deviceAttention);
        // 发送请求给管理员 *****************
        JSONObject pushDataOb = new JSONObject();
        pushDataOb.put("type", ConstPush.ACCOUNT_MESSAGE);

        JSONObject message = new JSONObject();
        message.put("imei", imei);
        message.put("type", "applicationRrecord");
        message.put("name", deviceName);
        message.put("phone", account.getPhone());
        message.put("msgTitle", msgTitle);
        message.put("msgContent", msgTitle);
        message.put("msgId", attention_id);
        message.put("timestamp", DateUtil.nowTimestampToLong());

        pushDataOb.put("message", message);
        kafkaService.sendPushToAccount(ConstKafka.ACCOUNT_TYPE_3, vo.getAdmin_openid(), pushDataOb);
        log.info(" openid: {} 申请绑定手表 imei: {} 推送给管理员审核成功 attention_id:{} ", vo.getOpenid(), imei, attention_id);
        JSONObject res = new JSONObject();
        res.put("attention_id", attention_id);
        return ResponseMap.success(res);
    }

    /**
     * 第三方登录:判断手机号是否能绑定
     *
     * @param phone
     * @param bindCheckType
     * @time: 12/20/21 9:10 PM
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap checkBindPhone(String phone, Integer bindCheckType, String openId) {
        if (VerificationCodeUtil.isPhoneNum(phone)) {
            JSONObject res = new JSONObject();
            //手机号登录
            Account account = this.findOne(new Query().addCriteria(Criteria.where("phone").is(phone)));
            if (account != null) {
                String tempId = null;
                String tempdes = null;
                if (bindCheckType == 1) {
                    tempId = account.getQqid();
                    tempdes = "QQ";
                } else if (bindCheckType == 2) {
                    if (!openId.equals(account.getId())) {
                        tempId = account.getWxid();
                        tempdes = "微信";
                    }
                } else if (bindCheckType == 7) {
                    tempId = account.getAppleid();
                    tempdes = "apple账号";
                }
                if (tempId != null) {
                    // 对应类型已有第三方Id
                    String failStr = MessageUtils.getLocale("API.request.register.bindFailDes1", tempdes);
                    return ResponseMap.fail(failStr);
                } else {
                    //已注册
                    res.put("registrationStatus", 1);
                }
            } else {
                //未注册
                res.put("registrationStatus", 0);
            }
            return ResponseMap.success(res);
        } else {
            return ResponseMap.fail("手机号不合规");
        }
    }


    /**
     * 添加设备关联
     *
     * @param vo
     * @time: 2020/11/9 17:07
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap addRelatedDevice(AddRelatedDeviceReqBodyFormVO vo, MultipartFile image, boolean isAPPReq) throws IOException {
        UpdateVO deviceUpdate = new UpdateVO();
        Device device = deviceService.findById(vo.getImei());
        Account account;
        //当前用户对于设备来说是 管理员绑定 还是 关注者绑定
        String identity = "admin"; //先默认管理员
        if (isAPPReq) {
            if (device == null) {
                log.error("Device is null! AddRelatedDeviceReqVO={}", vo.toString());
                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
            }
            if (device.getLossDevStatus() == 1) {
                return ResponseMap.fail(ResponseEnum.STATUS6001.getCode(), "API.request.deviceLossingNotBind");
            }
            if (vo.getPhone() == null) {
                log.error("imei:{} 设备号码不能为空 vo:{}", vo.getImei(), vo);
                //设备号码不能为空
                return ResponseMap.fail("设备号码不能为空");
            }
            account = findById(vo.getOpenid());
            if (account == null) {
                log.error("Account is null! AddRelatedDeviceReqVO={}", vo);
                return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
            }
            if (StringUtil.isBlank(account.getPhone())) {
                log.error("imei:{} openid:{} 用户号码不能为空 vo:{}", vo.getImei(), vo.getOpenid(), vo);
                //用户号码不能为空
                return ResponseMap.fail("用户号码不能为空,请完善后再绑定设备");
            }
//            String token = RedisClusterService.getJedisCluster().hget(vo.getImei(), ConstCacheField.TOKEN);
//            if (StringUtil.isBlank(device.getToken())) {
//                //判断设备是否联网激活 (有无token)
//                return ResponseMap.fail(ResponseEnum.STATUS600.getCode(), ResponseEnum.STATUS600.getMsg());
//            }
            if (CollectionUtil.containsKey(device.getUserMap(), account.getId())) {
                return ResponseMap.fail(ResponseEnum.STATUS4000.getCode(), "API.request.deviceAddRepeat");
            }
//            if (!token.equals(vo.getAuthcode())) {
//                return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceCodeInactive");
//            }
//            deviceUpdate.set("token", token);
//            deviceUpdate.set("time", RedisClusterService.getJedisCluster().hget(vo.getImei(), ConstCacheField.TOKEN_TIME));
            if (StringUtil.isNotBlank(vo.getName())) {
                deviceUpdate.set("name", vo.getName());
            }
            if (StringUtil.isNotBlank(vo.getPhone())) {
                deviceUpdate.set("phone", vo.getPhone());
            }
            if (StringUtil.isNotBlank(vo.getBirthday())) {
                deviceUpdate.set("birthday", vo.getBirthday());
            }
            if (StringUtil.isNotBlank(vo.getWeight())) {
                deviceUpdate.set("weight", vo.getWeight());
            }
            if (StringUtil.isNotBlank(vo.getHeight())) {
                deviceUpdate.set("height", vo.getHeight());
            }
            if (StringUtil.isNotBlank(vo.getSex())) {
                deviceUpdate.set("sex", vo.getSex());
            }
        } else {
            account = findById(vo.getOpenid());
            if (ObjectUtils.isEmpty(account)) {
                log.error("Account is null! AddRelatedDeviceReqVO={}", vo);
                return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
            }
        }

        Map<String, Map<String, Object>> userMap = device.getUserMap();
        if (userMap != null) {
            for (String key : userMap.keySet()) {
                JSONObject item = new JSONObject(userMap.get(key));
                // 如有已经有 且为 admin 那么后绑定的为成员
                if (item.containsKey("identity") && "admin".equals(item.getString("identity"))) {
                    identity = "member";
                }
            }
        }

        ResponseMap res = ResponseMap.success();
        //群聊的创建与绑定，取群组，没有群组则创建群组
        ChatGroup chatGroup = chatGroupService.getChatGroupByDeviceG(device.getChatGroupMap(), account.getId());
        UpdateVO accountUpdate = new UpdateVO();
        //用户绑定上设备 ==> "d" 字段 (deviceMap)，添加用户绑定的设备信息
        addDeviceHandle(account, chatGroup.getId(), device, accountUpdate, vo);
        //用户绑定上群组 ==> "g" 字段 (chatGroupMap)，添加
        addChatGroupHandle(account, chatGroup.getId(), accountUpdate);

        //多人群组的绑定
        UpdateVO chatGroupUpdate = new UpdateVO();
        //更新群组的member信息
        chatGroupService.addAccountHandle(chatGroup, account.getId(), chatGroupUpdate);
        //更新群组的device信息
        chatGroupService.addDeviceHandle(chatGroup, device.getId(), device.getVendor(), chatGroupUpdate);

        //单聊群组创建和绑定，单聊群组用用户openid+"&"+imei做id来关联用户和设备
        singleChatGroupService.createSingleG(account.getId(), device.getId());

        //设备的绑定 把用户加到这个 device 的 u 中 (同时也是增加家庭成员)(绑定关系)，发送添加视频、微聊联系人的指令并保存其指令数据
        Map<String, Object> currentUserMap = deviceService.addAccountHandle(device, account, chatGroup.getId(), deviceUpdate, vo);
        //增加device的g字段信息，更新设备群聊组的信息
        deviceService.addChatGroupHandle(device, chatGroup.getId(), deviceUpdate);
        // 上传头像文件，设备头像文件
        if (image != null && !image.isEmpty()) {
            /*if (StringUtil.isNotBlank(account.getImage()) && !account.getImage().contains("default.jpg")) {
          /*  if (StringUtil.isNotBlank(account.getImage()) && !account.getImage().contains("default.jpg")) {
                //删除旧文件
                baseBucketTool.delFile(BaseBucketTool.DEVICE_COMMON_PATH + account.getImage());
            }*/
            if (StringUtil.isNotBlank(device.getImage()) && !device.getImage().contains("default.jpg")) {
                //删除设备的旧头像文件
                baseBucketTool.delFile(BaseBucketTool.DEVICE_COMMON_PATH + device.getImage());
            }
            //生成文件名并上传
            String fileName = BaseBucketTool.createFileNameToJpg();
            baseBucketTool.upload(BaseBucketTool.DEVICE_COMMON_PATH + fileName, image, 1);
            deviceUpdate.set("image", fileName);
            res.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH + fileName);
        }
        //更新群组
        chatGroupService.updateById(new ObjectId(chatGroup.getId()), chatGroupUpdate);
        //推送群组消息给APP用户
        kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId(), null);
        // TODO Python改Java 后 不再在通讯录中保留 家庭人员信息, 要去重
        //检查通讯录, 如果通讯录中存在和绑定者号码一样，要把其删掉
        Map<String, Map<String, Object>> contactMap = device.getContactMap();
        if (contactMap != null && contactMap.size() > 0) {
            //迭代器移除
            Iterator<Map.Entry<String, Map<String, Object>>> contactMapIt = contactMap.entrySet().iterator();
            while (contactMapIt.hasNext()) {
                Map.Entry<String, Map<String, Object>> nextItem = contactMapIt.next();
                JSONObject item = new JSONObject(nextItem.getValue());
                if (item.containsKey("phone") && item.getString("phone") != null) {
                    if (item.getString("phone").equals(vo.getMy_phone())) {
                        //TODO 这里要暂时兼容Python 版本的, 只去重普通联系人 (后续应该直接去掉,不判断openid; 只留device.u 中的家庭人员信息)
                        if (!item.containsKey("openid")) {
                            //相同的移除
                            contactMapIt.remove();
                        }
                    }
                } else {
                    //通讯录联系人没有phone,有问题
                    log.error("imei:{} 通讯录联系人 没有phone, 有问题", vo.getImei());
                }
            }
            deviceUpdate.setMap("c", contactMap);
        }
        //关键的保存设备的修改信息的数据库更新操作
        deviceService.updateById(device.getId(), deviceUpdate);
        //更新账户信息
        this.updateById(account.getId(), accountUpdate);

        // 获取一下redis 中是否有这个设备标识头, 如果有的话证明是三基协议的
        String deviceHeadIdent = redisClusterService.hget(device.getId(), "deviceHead");
        if (deviceHeadIdent != null) {
            if ("admin".equals(identity)) {
                // centerPhone 设置管理员号码
                JSONObject centerPhoneData = new JSONObject();
                centerPhoneData.put("phone", vo.getMy_phone());
                //设置短信告警接收号码
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_CenterPhone, centerPhoneData, vo.getOpenid());
                JSONObject locationInterval = new JSONObject();
                Integer intervalSecond = Constant.DEFAULT_LOCATION_UPLOAD_INTERVAL;
                if (StringUtils.isNotEmpty(device.getModelId())) {
                    Models models = modelsService.findById(device.getModelId());
                    if (!ObjectUtils.isEmpty(models) && !ObjectUtils.isEmpty(models.getPositioningTimeInterval())) {
                        intervalSecond = Integer.valueOf(models.getPositioningTimeInterval());
                    }
                }
                locationInterval.put("second", intervalSecond.toString());
                // DEVICE_TYPE_LocationUploadInterval 定位间隔
                //kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_LocationUploadInterval, null, vo.getOpenid());
                kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_LocationUploadInterval, locationInterval, vo.getOpenid());
                Update devUpdate = new Update();
                devUpdate.set("locModeMin", intervalSecond);
                if (intervalSecond >= 60000) {
                    devUpdate.set("locMode", 3);
                } else if (intervalSecond >= 7200) {
                    devUpdate.set("locMode", 0);
                } else if (intervalSecond >= 3600) {
                    devUpdate.set("locMode", 1);
                } else if (intervalSecond >= 1800) {
                    devUpdate.set("locMode", 2);
                } else {
                    devUpdate.set("locMode", 0);
                }
                deviceService.updateById(device.getId(), devUpdate);
                JSONObject requestLocationData = new JSONObject();
                // 如果在线, requestLocation 主动定位一下
                String onlineStatus = redisClusterService.hget(device.getId(), "onlineStatus");
                if ("1".equals(onlineStatus)) {
                    kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_RequestLocation, null, vo.getOpenid());
                } else {
                    try {
                        //TODO 数据库中存在, 但是Redis中没有,补偿措施
                        String v = redisClusterService.hget(device.getId(), "v");
                        if (v == null) {
                            redisClusterService.hset(device.getId(), "v", "20000");
                            redisClusterService.hset(device.getId(), "agent", "" + device.getAgent());
                            log.info("imei:" + device.getId() + " 数据库中存在, 但是Redis中没有,补偿措施,设置 标识v 和 agent:" + device.getAgent());
                        }
                    } catch (Exception e) {
                        log.error("imei:" + device.getId() + " 数据库中存在, 但是Redis中没有,补偿措施,设置 标识v 和 agent:" + device.getAgent() + "失败:" + e.getMessage(), e);
                    }
                }
            }
        }
        //设置一个字段表明 绑定激活
        redisClusterService.hset(device.getId(), "related_status", "1");
        //更新一下 下发给手表的 通信录 (16指令)，添加联系人
        JSONObject kafkaJson = new JSONObject();
        kafkaJson.put("name", vo.getRelationship());
        kafkaJson.put("phone", account.getPhone());
        if (!ObjectUtils.isEmpty(currentUserMap.get("image")) && !StringTool.isInteger(currentUserMap.get("image").toString())) {
            String fileName = currentUserMap.get("image").toString();
            kafkaJson.put("avator", BaseBucketTool.DEVICE_COMMON_PATH + fileName);
        }
        //发送电话本指令
        kafkaJson.put("serialNum", currentUserMap.get("serialNum").toString());
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_ADD_CONTACT, kafkaJson, vo.getOpenid());
        //handleFlag  1:添加 2:删除 3:修改
        /*kafkaJson.put("handleFlag", 1);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_Contact, kafkaJson, vo.getOpenid());*/
        if (Const.INT_1.equals(device.getIsWatchCustomFunction()) && StringUtil.isNotBlank(device.getWatchCustomFunction())) {
            if (device.getWatchCustomFunction().contains("DEVTYPE:")) {
                String[] tempArr = device.getWatchCustomFunction().split("DEVTYPE:");
                tempArr = tempArr[tempArr.length - 1].split(",");
                if (StringUtil.isNumeric(tempArr[0])) {
                    res.put("devOrientGroup", Integer.parseInt(tempArr[0]));
                }
            }
            if (device.getWatchCustomFunction().contains("YDYP")) {
                //对接了 中国移动云盘 的
                //绑定手表后，展示云盘授权弹窗，增加一条私聊通知云盘授权
                String tempStr = "{\"type\":\"157\"}";
                tcpService.type_157(device, JSONObject.parseObject(tempStr), false);
            }
        }
        //log.info(" 添加关联设备成功 ");
        userKeyActionService.add(6, vo.getOpenid(), "绑定设备:" + device.getId());

        // 构造指令数据
        JSONObject commandData = new JSONObject();
        commandData.put("openId", vo.getOpenid());
        commandData.put("status", 1);
        kafkaService.emitDeviceDemand(device.getId(), ConstKafka.DEVICE_TYPE_BindDevice, commandData, vo.getOpenid());
        return res;
    }


    public boolean checkAccountIsAdminWithImei(Account account, String imei) {
        Map<String, Object> devMap = account.getDeviceMap().get(imei);
        if (!"admin".equals(devMap.get("identity").toString())) {
            return false;
        }
        return true;
    }

    public boolean checkAccountIsAdminWithImei(String openid, String imei) {
        Account account = this.findById(openid);
        return this.checkAccountIsAdminWithImei(account, imei);
    }

    public static void main(String[] args) {
//        Map<String, Map<String, Object>> contactMap = new HashMap<>();
//        Map<String, Object> tempValue1 = new HashMap<>();
//        tempValue1.put("tempKey1", "tempValue1");
//        Map<String, Object> tempValue2 = new HashMap<>();
//        tempValue2.put("tempKey2", "tempValue2");
//        contactMap.put("aaa11", tempValue1);
//        contactMap.put("bbb22", tempValue2);
//
//        log.info("前 contactMap: {}", contactMap.toString());
//
////        for (String key: contactMap.keySet()) {
////            JSONObject item = new JSONObject(contactMap.get(key));
////            if(key.equals("bbb22")){
////                contactMap.remove(key);
////            }
////        }
//        Iterator<Map.Entry<String, Map<String, Object>>> iterator = contactMap.entrySet().iterator();
//        while (iterator.hasNext()){
//            Map.Entry<String, Map<String, Object>> item = iterator.next();
////            item.getKey();
////            item.getValue();
//            if(item.getKey().equals("bbb22") || item.getKey().equals("aaa11")){
//                iterator.remove();
//            }
//
//        }
//
//        log.info("后 contactMap: {}", contactMap.toString());
    }

    /**
     * 用户绑定上设备 ==> "d" 字段 (deviceMap)
     *
     * @param account
     * @param chatGroupId
     * @param device
     * @param updateAccount
     * @param vo
     * @time: 2020/11/12 17:26
     * @author: Rock
     * @version 1.0
     */
    public void addDeviceHandle(Account account, String chatGroupId, Device device, UpdateVO updateAccount, AddRelatedDeviceReqBodyFormVO vo) {
        String deviceId = device.getId();
        Integer deviceV = device.getVendor();
        if (account.getDeviceMap() == null) {
            account.setDeviceMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", System.currentTimeMillis() / 1000d);
        sub.put("g", chatGroupId);
        sub.put("v", deviceV == null ? 20000 : deviceV);
        //默认是关注者
        sub.put("identity", "member");

        Map<String, Map<String, Object>> userMap = device.getUserMap();
        if (userMap != null) {
            for (String key : userMap.keySet()) {
                JSONObject item = new JSONObject(userMap.get(key));
                // 如有已经有 且为 admin 那么后绑定的为成员
                if (item.containsKey("identity")) {
                    if ("admin".equals(item.getString("identity"))) {
                        sub.put("identity", "member");
                    }
                } else {
                    sub.put("identity", "admin");
                }
            }
            if (userMap.size() == 1) {
                //如果只有一个的且为 游客的情况,要成为管理员
                for (String key : userMap.keySet()) {
                    JSONObject item = new JSONObject(userMap.get(key));
                    if (item.containsKey("identity") && "visitor".equals(item.getString("identity"))) {
                        sub.put("identity", "admin");
                    }
                }
            }
        } else {
            //如果设备没有绑定者,第一个就是管理员
            sub.put("identity", "admin");
        }
        if (vo.getRelationship() != null) {
            sub.put("relationship", vo.getRelationship());
        }
        if (vo.getRelationship_image_id() != null) {
            sub.put("relationship_image_id", vo.getRelationship_image_id());
        }

        account.getDeviceMap().put(deviceId, sub);
        updateAccount.setMap("d", account.getDeviceMap());
    }

    /**
     * 用户绑定上群组 ==> "g" 字段 (chatGroupMap)
     *
     * @param account
     * @param chatGroupId
     * @time: 2020/11/12 17:33
     * @author: Rock
     * @version 1.0
     */
    public void addChatGroupHandle(Account account, String chatGroupId, UpdateVO updateAccount) {
        if (account.getChatGroupMap() == null) {
            account.setChatGroupMap(new HashMap<>());
        }
        Map<String, Object> sub = new HashMap<>();
        sub.put("t", System.currentTimeMillis() / 1000d);
        account.getChatGroupMap().put(chatGroupId, sub);
        updateAccount.setMap("g", account.getChatGroupMap());
    }

    /**
     * 查询用户关联设备
     *
     * @param vo
     * @param userAgent
     * @param version
     * @param iosProduction
     * @param packageName
     * @time: 2021/1/27 15:08
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getRelatedDevice(BaseAccountReqVO vo, String userAgent, String version, String iosProduction, String packageName) {
        //iOS 系统版本  默认还是支持VoIP 的
        String sysVersion = "";
        //手机型号
        String phoneType = "";
        // 手机类型 (目前只有 android 和 ios)
        String mobileType = "";
        // APP 的版本
        if (StringUtil.isBlank(version)) {
            version = "1.0.0";
        }
        //iOS APP 的推送环境
        Account account = this.findById(vo.getOpenid());
        HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (account == null) {
            log.error("Account is null. id={}. getRelatedDevice vo={}", vo.getOpenid(), vo);
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        log.info("用户查询关联设备接口 vo:{}", vo);
        //请求头数据解析处理
        if (StringUtil.isNotBlank(userAgent)) {
            String[] sysInfoList = userAgent.split(";");
            String[] tempArr = sysInfoList[0].split("\\(");
            phoneType = tempArr[tempArr.length - 1];
            if (userAgent.contains("iOS")) {
                //IOS User-Agent: watch/1.6.0 (iPhone; iOS 11.2.1; Scale/2.00)
                packageName = req.getHeader("BundleId");
                for (String value : sysInfoList) {
                    if (value != null && value.contains("iOS")) {
                        tempArr = value.split(" ");
                        sysVersion = tempArr[tempArr.length - 1];
                    } else if (value != null && value.contains(" (")) {
                        //watch/1.6.0 (iPhone
                        String[] verSplit = value.split(" ")[0].split("/");
                        version = verSplit[verSplit.length - 1];
                    }
                }
                if (iosProduction == null) {
                    //ios默认 生产环境
                    iosProduction = "produce";
                }
                mobileType = "ios";
            } else if (userAgent.contains("Android")) {
                packageName = req.getHeader("packageName");
                //android user-agent : watch/1.4.0(KKG-AN70;Android;11)
                sysVersion = sysInfoList[sysInfoList.length - 1].replace(")", "");
                tempArr = tempArr[0].split("/");
                version = tempArr[tempArr.length - 1];
                mobileType = "android";
            }
        }
        if (StringUtil.isNotBlank(vo.getOpenid()) && StringUtil.isNotBlank(sysVersion)
                && StringUtil.isNotBlank(mobileType)) {
            openidConfigService.checkUpdate(vo.getOpenid(), mobileType + "-" + phoneType,
                    sysVersion, version, iosProduction, packageName, account.getRegistrationID(), account.getOldRegistrationID());
        }
        //用户与设备关联的聊天群组信息
        Map<String, Object> accountDeviceGroupRelate = new HashMap<>();
        //按每个设备所属的代理商查找设备数据
        Map<String, List<String>> deviceVendorData = new HashMap<>();
        //该账号拥有的设备
        if (CollectionUtil.isNotEmpty(account.getDeviceMap())) {
            for (Map.Entry<String, Map<String, Object>> entry : account.getDeviceMap().entrySet()) {
                if (!deviceVendorData.containsKey(entry.getValue().get("v"))) {
                    deviceVendorData.put(entry.getValue().get("v").toString(), new ArrayList<>());
                }
                deviceVendorData.get(entry.getValue().get("v").toString()).add(entry.getKey());

                accountDeviceGroupRelate.put(entry.getKey(), entry.getValue().get("g"));
            }
        }
        //当前账号下的群组列表
        if (CollectionUtil.isNotEmpty(account.getChatGroupMap())) {
            List<ChatGroup> chatGroups = chatGroupService.listByIds(CollectionUtil.stringToObjectId(account.getChatGroupMap().keySet()));
            for (ChatGroup cg : chatGroups) {
                if (CollectionUtil.isNotEmpty(cg.getDeviceMap())) {
                    for (Map.Entry<String, Map<String, Object>> entry : cg.getDeviceMap().entrySet()) {
                        if (!deviceVendorData.containsKey(entry.getValue().get("v").toString())) {
                            deviceVendorData.put(entry.getValue().get("v").toString(), new ArrayList<>());
                        }
                        deviceVendorData.get(entry.getValue().get("v").toString()).add(entry.getKey());
                        accountDeviceGroupRelate.put(entry.getKey(), cg.getId());
                    }
                }
            }
        }

        List<Map<String, Object>> chatGroupDevices = new ArrayList<>();
        List<Map<String, Object>> ownedDevices = new ArrayList<>();
        for (Map.Entry<String, List<String>> entry : deviceVendorData.entrySet()) {
            List<Device> devices = deviceService.listByIds(entry.getValue());
            for (Device device : devices) {
                //判断是否和device 中的 g 是否对应 不对应需要以device中的为准
                boolean accountGisBad = false;
                if (CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
                    String key = device.getChatGroupMap().keySet().iterator().next();
                    if (!key.equals(accountDeviceGroupRelate.get(device.getId()))) {
                        accountGisBad = true;
                    }
                } else {
                    accountGisBad = true;
                }
                if (accountGisBad) {
                    log.error("openid={} 这个群组ID :{} 是异常的, 需要清除掉", vo.getOpenid(), accountDeviceGroupRelate.get(device.getId()));
                    chatGroupService.del(accountDeviceGroupRelate.get(device.getId()).toString(), account.getId());
                }
                String deviceName = deviceService.getDeviceName(device);
                JSONObject data = new JSONObject();
                data.put("imei", device.getId());
                data.put("groupid", accountDeviceGroupRelate.get(device.getId()));
                //单聊群ID
                data.put("singleGroupId", account.getId() + "&" + device.getId());
                data.put("isNewWechatProtocol", device.getIsNewWechatProtocol());
                data.put("vendor", 20000);
                data.put("battery", device.getBattery());
                data.put("curr_level", device.getCurrLevel());
                data.put("max_level", device.getMaxLevel());
                data.put("name", deviceName);
                data.put("phone", StringUtil.isBlank(device.getPhone()) ? "" : device.getPhone());
                data.put("my_phone", account.getPhone());
                data.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH
                        + (StringUtil.isBlank(device.getImage()) ? Const.DEFAULT_JPG : device.getImage()));
                data.put("sex", StringUtil.isBlank(device.getSex()) ? "girl" : device.getSex());
                data.put("grade", StringUtil.isBlank(device.getGrade()) ? "" : device.getGrade());
                data.put("birthday", StringUtil.isBlank(device.getBirthday()) ? "" : device.getBirthday());
                data.put("height", "" + device.getHeight());
                data.put("weight", "" + device.getWeight());
                //检查 并获得 identity
                data.put("identity", checkDevUserInfoGetIdentity(device, vo.getOpenid()));

                data.put("relationship", "亲人");
                data.put("relationship_image_id", 9);
                Object relationship = device.getUserMap().get(vo.getOpenid()).get("relationship");
                if (relationship != null) {
                    data.put("relationship", relationship);
                }
                Object relationship_image_id = device.getUserMap().get(vo.getOpenid()).get("relationship_image_id");
                if (relationship_image_id != null) {
                    data.put("relationship_image_id", relationship_image_id);
                }
                //调用 检查 功能方法，设置设备支持的功能
                deviceService.checkDeviceFunctions(device, data);
                /*if(CommonUtil.compareVersions(version,"1.1.4") > 0){
                    deviceService.settingDeviceFunction(device,data);
                } else {
                    deviceService.checkDeviceFunctions(device, data);
                }*/
                if (CollectionUtil.containsKey(device.getUserMap(), vo.getOpenid())) {
                    //当前这个用户绑定这个表的时间
                    Object time = device.getUserMap().get(vo.getOpenid()).get("t");
                    data.put("time", StringUtil.isNumeric(time) ? new Double(time.toString()).longValue() : 0);
                } else {
                    data.put("time", 0);
                }
                if (CollectionUtil.containsKey(account.getDeviceMap(), device.getId())) {
                    chatGroupDevices.add(data); //赛诺用的是此字段 chatGroupDevices
                } else {
                    ownedDevices.add(data);
                }
                //TODO 这个是补偿的方法,后续是没有必要的,只是为了兼容修复线上以前的BUG产生的问题.
                checkRedisInfo(device);
            }
        }
        //根据time字段降序排序
        CollectionUtil.sortDouble(chatGroupDevices, "time", 2);
        CollectionUtil.sortDouble(ownedDevices, "time", 2);
        //这里暂时更新附带上来凭证的时间 , 不管新旧 (后续等APP迭代完成后需去掉)
        if (StringUtil.isNotBlank(vo.getAccesstoken())) {
            setAccessTokenCache(vo.getAccesstoken(), vo.getOpenid());
        }
        ResponseMap res = ResponseMap.success();
        res.put("chatGroupDevices", chatGroupDevices);
//        res.put("ownedDevices", ownedDevices);
        res.put("ad_swtich", "true");
        //调用频率，1为一天调一次，2位随时更新
        res.put("ad_rate", 1);
        //生成新的凭证
        //重置缓存时间
        setAccessTokenCache(account.getAccesstoken(), vo.getOpenid());
        Update update = new Update();
        update.set("at", DateUtil.nowTimestampToLong());
        update.set("sysVersion", sysVersion);
        update.set("version", version);
        update.set("mobile_type", mobileType);
        if (packageName != null) {
            update.set("packageName", packageName);
        }
        // trackingInfo: IP+ time + phoneType
        String ip = req.getHeader("x-forwarded-for");
        if (StringUtils.isEmpty(ip)) {
            ip = req.getRemoteAddr();
        }
        update.set("trackingInfo", ip + "&" + DateUtil.dateToStr(new Date(), DateUtil.FORMAT_3) + "&" + phoneType);
        //更新时间
        this.updateById(account.getId(), update);
        res.put("accesstoken", account.getAccesstoken());
        return res;
    }

    //TODO 这个是补偿的方法,后续是没有必要的,只是为了兼容修复线上以前的BUG产生的问题.
    public void checkRedisInfo(Device device) {
        try {
            //TODO 数据库中存在, 但是Redis中没有,补偿措施
            String v = redisClusterService.hget(device.getId(), "v");
            if (v == null) {
                redisClusterService.hset(device.getId(), "v", "20000");
                redisClusterService.hset(device.getId(), "agent", "" + device.getAgent());
                log.info("imei:" + device.getId() + " 数据库中存在, 但是Redis中没有,补偿措施,设置 标识v 和 agent:" + device.getAgent());
            }
        } catch (Exception e) {
            log.error("imei:" + device.getId() + " 数据库中存在, 但是Redis中没有,补偿措施,设置 标识v 和 agent:" + device.getAgent() + "失败:" + e.getMessage(), e);
        }
    }

    //TODO 获取identity 并 检查修复 device表中的 u 字段中的用户 的identity没有的时候更新一下
    public String checkDevUserInfoGetIdentity(Device device, String openid) {
        Map<String, Map<String, Object>> oldUserMap = device.getUserMap();
        Map<String, Object> openidUserInfo = oldUserMap.get(openid);
        Object identity = openidUserInfo.get("identity");
        if (identity != null) {
            return identity.toString();
        }
        //检查通讯录中 有openid 的用户, 从中取得原本的identity.
        Map<String, Map<String, Object>> contactMap = device.getContactMap();
        for (Map<String, Object> item : contactMap.values()) {
            Object itemOpenid = item.get("openid");
            if (openid.equals(itemOpenid)) {
                identity = item.get("identity");
            }
        }
        //如没有,都赋值是关注者member,不是管理员
        if (identity == null) {
            identity = "member";
        }

        openidUserInfo.put("identity", identity);
        oldUserMap.put(openid, openidUserInfo);
        Update updateDevice = new Update();
        updateDevice.set("u", oldUserMap);
        log.info("imei={} 检查修复 device表中的 u 字段中的用户({}) 的identity没有的时候更新为: {} 更新进device中的u:{}", device.getId(), openid, identity, oldUserMap);
        this.updateById(device.getId(), updateDevice);
        return identity.toString();
    }


    /**
     * @param [vo]
     * @return com.iot08.common.utils.response.ResponseMap
     * @description 查询设备指令功能
     * @author cwj
     * @date @time 2025/1/14 11:41
     **/
    public ResponseMap queryDeviceFunction(DeviceFunctionReqVo vo) {
        Device device = deviceService.findById(vo.getImei());
        return deviceService.queryDeviceFunction(device);
    }

    /**
     * 解除设备关联 (如果是管理员(或者imei游客)解绑,是恢复出厂设置)
     *
     * @param vo
     * @time: 2020/12/3 9:07
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap delRelatedDevice(ImeiParamReqBodyFormVO vo) {
        Account account = this.findById(vo.getOpenid());
        if (account == null) {
            log.info("Account is null. params={}", vo);
            operateLogService.saveOperateLog(vo.getOpenid(), null, vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "解绑设备" + vo.getImei() + "失败，系统中不存在" + vo.getOpenid() + "账户信息");
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        if (vo.getImei().equals(account.getImei())) {
            log.info("由于您使用IMEI账号登录，将不能解绑自身设备");
            return ResponseMap.fail(ResponseEnum.STATUS600.getCode(), "由于您使用IMEI账号登录，将不能解绑自身设备");
        }
        Device device = deviceService.findById(vo.getImei());
        if (device == null) {
            log.info("Device is null. params={}", vo);
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备失败，系统中不存在设备" + vo.getImei());
            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
        }
        //如果强制解绑就不用检查设备是否在线了
        if (ObjectUtils.isEmpty(vo.getIsForceUnbind()) || (!ObjectUtils.isEmpty(vo.getIsForceUnbind()) && !vo.getIsForceUnbind().equals(1))) {
            //检查设备是否在线，不在线不能下发
            String onlineStatus = redisClusterService.hget(vo.getImei(), ConstCacheField.ONLINE_STATUS);
            if (!"1".equals(onlineStatus)) {
                return ResponseMap.fail(ResponseEnum.STATUS5001.getCode(), ResponseEnum.STATUS5001.getMsg());
            }
        }
        //检查设备是否在线
        if (device.getLossDevStatus() == 1) {
            log.info("The device is in a lost state and cannot be unbound. params={}", vo);
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备失败，设备" + vo.getImei() + "处于挂失状态，无法解绑");
            //"设备处于挂失状态，无法解绑"
            return ResponseMap.fail(ResponseEnum.STATUS6001.getCode(), "API.request.deviceLossingNotUnbind");
        }

        //构建 删除家庭成员的 请求体
        DeviceDelMemberReqVO delMemberReqVO = new DeviceDelMemberReqVO();
        delMemberReqVO.setId(vo.getOpenid());
        delMemberReqVO.setOpenid(vo.getOpenid());
        delMemberReqVO.setImei(vo.getImei());
        //删除成员并发送删除联系人等指令（kafka）给设备
        deviceService.delMember(delMemberReqVO);
        String identity = this.getAccountIdentityWithDevice(account, device.getId());
        if ("admin".equals(identity)) {
            log.info("imei:{} 的管理员:{} 解绑所有的用户", device.getId(), account.getId());
            deviceService.reset(device, account.getId(), "app");
            //userKeyActionService.add(8, account.getId(), String.format("imei:%s 的管理员:%s 解绑所有的用户",device.getId(), account.getId()));
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 1, vo.toString(),
                    "AccountService.delRelatedDevice", String.format("imei:%s 的管理员:%s 解绑所有的用户成功", device.getId(), account.getId()));
        } else {
            //userKeyActionService.add(7,account.getId(), "解绑设备"+device.getId());
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 1, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备" + vo.getImei() + "成功");
        }
        return ResponseMap.success();

//        UpdateVO updateDevice = new UpdateVO();
//        UpdateVO updateAccount = new UpdateVO();
//
//        //清除Account 表中的 d 字段
//        if (CollectionUtil.removeMapKey(account.getDeviceMap(), device.getId())) {
//            updateAccount.setMap("d", account.getDeviceMap());
//        }
//        //清除Device 表中的 u 字段 中的这个account 账号对应 u , 返回清除的u中包含有g 的信息
//        Map<String, Object> delAccountToDevice = CollectionUtil.removeMapKeyData(device.getUserMap(), account.getId());
//        if (delAccountToDevice != null) {
//            updateDevice.setMap("u", device.getUserMap());
//        }
//        //清除Device表 中 checked_openids 中的 openid
//        Map<String, Object> delCheckedOpenidToDevice = CollectionUtil.removeMapKeyData(device.getCheckedOpenidsMap(), account.getId());
//        if (delCheckedOpenidToDevice != null) {
//            updateDevice.setMap("checked_openids", device.getCheckedOpenidsMap());
//        }
//
//        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
//            //将设备从与该用户关联的群组中移出
//            if (delAccountToDevice != null && delAccountToDevice.containsKey("g")) {
//                ChatGroup chatGroup = chatGroupService.findById(new ObjectId(delAccountToDevice.get("g").toString()));
//                if (chatGroup != null) {
//                    if (CollectionUtil.removeMapKey(chatGroup.getMemberMap(), account.getId())) {
//                        UpdateVO updateChatGroup = new UpdateVO();
//                        updateChatGroup.setMap("m", chatGroup.getMemberMap());
//                        chatGroupService.updateById(new ObjectId(chatGroup.getId()), updateChatGroup);
//                    }
//                    if (CollectionUtil.removeMapKey(account.getChatGroupMap(), chatGroup.getId())) {
//                        updateAccount.setMap("g", account.getChatGroupMap());
//                        updateAccount.unset(chatGroup.getId());
//                    }
//                }
//            }
//        } else {
//            //将没有所有者的设备 移出所有聊天群
//            if (CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
//                Set<ObjectId> chatGroupIds = CollectionUtil.stringToObjectId(device.getChatGroupMap().keySet());
//                List<ChatGroup> chatGroups = chatGroupService.listByIds(chatGroupIds);
//                for (ChatGroup chatGroup : chatGroups) {
//                    chatGroupService.delDeviceHandle(chatGroup, device.getId());
//                    if (CollectionUtil.removeMapKey(device.getChatGroupMap(), chatGroup.getId())) {
//                        updateDevice.setMap("g", device.getChatGroupMap());
//                    }
//                    if (CollectionUtil.removeMapKey(account.getChatGroupMap(), chatGroup.getId())) {
//                        updateAccount.setMap("g", account.getChatGroupMap());
//                        updateAccount.unset(chatGroup.getId());
//                    }
//                }
//            }
//        }
//        //更新操作
//        deviceService.updateById(device.getId(), updateDevice);
//        this.updateById(account.getId(), updateAccount);
//        return ResponseMap.success();
    }


    /**
     * @param [vo]
     * @return com.iot08.common.utils.response.ResponseMap
     * @description 解绑
     * @author cwj
     * @date @time 2025/7/10 9:00
     **/

    public ResponseMap delUserRelatedDevice(ImeiParamReqBodyFormVO vo, String delRelatedOpenid) {
        Account account = this.findById(delRelatedOpenid);
        if (account == null) {
            log.info("Account is null. params={}", vo);
            operateLogService.saveOperateLog(vo.getOpenid(), null, vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "解绑设备" + vo.getImei() + "失败，系统中不存在" + vo.getOpenid() + "账户信息");
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        if (vo.getImei().equals(account.getImei())) {
            log.info("由于您使用IMEI账号登录，将不能解绑自身设备");
            return ResponseMap.fail(ResponseEnum.STATUS600.getCode(), "由于您使用IMEI账号登录，将不能解绑自身设备");
        }
        Device device = deviceService.findById(vo.getImei());
        if (device == null) {
            log.info("Device is null. params={}", vo);
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备失败，系统中不存在设备" + vo.getImei());
            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
        }
        //如果强制解绑就不用检查设备是否在线了
        if (ObjectUtils.isEmpty(vo.getIsForceUnbind()) || (!ObjectUtils.isEmpty(vo.getIsForceUnbind()) && !vo.getIsForceUnbind().equals(1))) {
            //检查设备是否在线，不在线不能下发
            String onlineStatus = redisClusterService.hget(vo.getImei(), ConstCacheField.ONLINE_STATUS);
            if (!"1".equals(onlineStatus)) {
                return ResponseMap.fail(ResponseEnum.STATUS5001.getCode(), ResponseEnum.STATUS5001.getMsg());
            }
        }
        //检查设备是否在线
        if (device.getLossDevStatus() == 1) {
            log.info("The device is in a lost state and cannot be unbound. params={}", vo);
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 0, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备失败，设备" + vo.getImei() + "处于挂失状态，无法解绑");
            //"设备处于挂失状态，无法解绑"
            return ResponseMap.fail(ResponseEnum.STATUS6001.getCode(), "API.request.deviceLossingNotUnbind");
        }

        //构建删除家庭成员的 请求体
        DeviceDelMemberReqVO delMemberReqVO = new DeviceDelMemberReqVO();
        delMemberReqVO.setId(delRelatedOpenid);
        delMemberReqVO.setOpenid(vo.getOpenid());
        delMemberReqVO.setImei(vo.getImei());
        if (vo.getOpenid().equals(delRelatedOpenid)) {
            delMemberReqVO.setCallType(0);
        } else {
            delMemberReqVO.setCallType(1);
        }
        //删除成员并发送删除联系人等指令（kafka）给设备
        deviceService.delMember(delMemberReqVO);
        String identity = this.getAccountIdentityWithDevice(account, device.getId());
        if ("admin".equals(identity)) {
            log.info("imei:{} 的管理员:{} 解绑所有的用户", device.getId(), account.getId());
            deviceService.reset(device, account.getId(), "app");
            //userKeyActionService.add(8, account.getId(), String.format("imei:%s 的管理员:%s 解绑所有的用户",device.getId(), account.getId()));
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 1, vo.toString(),
                    "AccountService.delRelatedDevice", String.format("imei:%s 的管理员:%s 解绑所有的用户成功", device.getId(), account.getId()));
        } else {
            //userKeyActionService.add(7,account.getId(), "解绑设备"+device.getId());
            operateLogService.saveOperateLog(account.getId(), account.getName(), vo.getImei(), "设备解绑", 1, vo.toString(),
                    "AccountService.delRelatedDevice", "用户:" + account.getId() + "解绑设备" + vo.getImei() + "成功");
        }
        return ResponseMap.success();
    }


    /**
     * 进入聊天群组
     *
     * @param vo
     * @time: 2020/11/19 10:58
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap addRelatedChatGroup(AddRelatedChatGroupReqVO vo) {
        //TODO 暂缓实现,先注释,邀请进去聊天群组
//        Account account = this.findById(vo.getOpenid());
//        if (account == null) {
//            log.info("Account is null. params={}", vo.toString());
//            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
//        }
//        UpdateVO updateDevice = new UpdateVO();
//        UpdateVO updateAccount = new UpdateVO();
//        UpdateVO updateChatGroup = new UpdateVO();
//        String token = "";
//        ChatGroup chatGroup = null;
//        if (vo.getGroupid() != null && vo.getGroupid().startsWith("imei:")) {
//            String imei = vo.getGroupid().split(":")[1];
//            Device device = deviceService.findById(imei);
//            if (device == null) {
//                log.info("Device is null. params={}", vo.toString());
//                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), imei);
//            }
//            token = RedisClusterService.getJedisCluster().hget(imei, ConstCacheField.TOKEN);
//            if (StringUtil.isNotBlank(token)) {
//                if (StringUtil.isBlank(device.getToken())) {
//                    updateDevice.set("token", token);
//                    updateDevice.set("ttime", RedisClusterService.getJedisCluster().hget(imei, ConstCacheField.TOKEN_TIME));
//                }
//                if (!token.equals(vo.getInviteToken())) {
//                    return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceCodeInactive");
//                }
//            }
//            chatGroup = chatGroupService.getChatGroupByDeviceG(device.getChatGroupMap(), account.getId());
//            addDeviceHandle(account, chatGroup.getId(), device.getId(), device.getVendor(), updateAccount);
//            addChatGroupHandle(account, chatGroup.getId(), updateAccount);
//            chatGroupService.addAccountHandle(chatGroup, account.getId(), updateChatGroup);
//            chatGroupService.addDeviceHandle(chatGroup, device.getId(), device.getVendor(), updateChatGroup);
//            deviceService.addAccountHandle(device, account.getId(), chatGroup.getId(), updateDevice);
//            deviceService.addChatGroupHandle(device, chatGroup.getId(), updateDevice);
//            //更新操作
//            deviceService.updateById(device.getId(), updateDevice);
//        } else {
//            chatGroup = chatGroupService.findById(new ObjectId(vo.getGroupid()));
//            boolean verify = true;
//            if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
//                List<Device> devices = deviceService.listByIds(chatGroup.getDeviceMap().keySet());
//                for (Device device : devices) {
//                    token = RedisClusterService.getJedisCluster().hget(device.getId(), ConstCacheField.TOKEN);
//                    if (vo.getInviteToken().equals(token)) {
//                        verify = false;
//                        break;
//                    }
//                }
//            }
//            if (verify) {
//                return ResponseMap.fail(ResponseEnum.STATUS403.getCode(), "API.request.deviceCodeInactive");
//            }
//            addChatGroupHandle(account, chatGroup.getId(), updateAccount);
//            chatGroupService.addAccountHandle(chatGroup, account.getId(), updateChatGroup);
//        }
//        //更新操作
//        chatGroupService.updateById(new ObjectId(chatGroup.getId()), updateChatGroup);
//        this.updateById(account.getId(), updateAccount);
//        //发送kafka
//        kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId());
//        ResponseMap res = ResponseMap.success();
//        res.put("groupid", chatGroup.getId());
//        return res;
        return ResponseMap.fail();
    }

    /**
     * 查询用户聊天群组
     *
     * @param vo
     * @time: 2020/11/13 15:47
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap getRelatedChatGroup(UserChatGroupGetInfoReqVO vo) {
        Account account = findById(vo.getOpenid());
        if (account == null) {
            log.info("Account is null. params={}", vo.toString());
            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
        }
        List<Object> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(account.getChatGroupMap())) {
            Set<ObjectId> chatGroupIds = CollectionUtil.stringToObjectId(account.getChatGroupMap().keySet());
            List<ChatGroup> chatGroups = chatGroupService.listByIds(chatGroupIds);
            for (ChatGroup chatGroup : chatGroups) {
                Map<String, Object> map = new HashMap<>();
                map.put("groupid", chatGroup.getId());
                list.add(map);
            }
        }
        List<Object> singleChatGroups = new ArrayList<>();
        if (account.getDeviceMap() != null) {
            for (String devImei :
                    account.getDeviceMap().keySet()) {
                Map<String, Object> tempMap = new HashMap<>();
                tempMap.put("groupid", account.getId() + "&" + devImei);
                tempMap.put("imei", devImei);
                singleChatGroups.add(tempMap);
            }
        }

        ResponseMap res = ResponseMap.success();
        res.put("chatGroups", list);
        res.put("singleChatGroups", singleChatGroups);
        return res;
    }

    /**
     * 退出聊天群组
     *
     * @param vo
     * @time: 2020/11/13 15:56
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap delRelatedChatGroup(GroupidParamReqVO vo) {
        //TODO 暂缓实现,先注释,邀请进去聊天群组
//        Account account = findById(vo.getOpenid());
//        if (account == null) {
//            log.info("Account is null. params={}", vo.toString());
//            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
//        }
//        ObjectId chatGroupObjectId = new ObjectId(vo.getGroupid());
//        ChatGroup chatGroup = chatGroupService.findById(chatGroupObjectId);
//        if (chatGroup != null && !CollectionUtil.containsKey(account.getChatGroupMap(), chatGroup.getId())) {
//            return ResponseMap.fail(ResponseEnum.STATUS4003.getCode(), String.format(ResponseEnum.STATUS4003.getMsg(), account));
//        }
//        UpdateVO update = new UpdateVO();
//        CollectionUtil.removeMapKey(account.getChatGroupMap(), chatGroup.getId());
//        update.setMap("g", account.getChatGroupMap());
//        update.unset(chatGroup.getId());
//        this.updateById(account.getId(), update);
//        if (CollectionUtil.removeMapKey(chatGroup.getMemberMap(), account.getId())) {
//            update = new UpdateVO();
//            update.setMap("m", chatGroup.getMemberMap());
//            chatGroupService.updateById(chatGroupObjectId, update);
//            //发送kafk
//            kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_REMOVE, account.getId());
//        }
//        return ResponseMap.success();
        return ResponseMap.fail();
    }

    /**
     * @param chatGroupId
     * @param ids
     * @time: 2020/12/21 10:25
     * @author: Rock
     * @version 1.0
     */
    public void delChatGroup(String chatGroupId, Set<String> ids) {
        List<Account> accountList = this.listByIds(ids);
        for (Account account : accountList) {
            if (account != null && CollectionUtil.removeMapKey(account.getChatGroupMap(), chatGroupId)) {
                UpdateVO update = new UpdateVO();
                update.setMap("g", account.getChatGroupMap());
                update.unset(chatGroupId);
                this.updateById(account.getId(), update);
            }
        }
    }

//    /**
//     * 根据openid查询当前是否正在视频
//     * @param vo
//     * @time: 2021-10-21 11:23
//     * @author: Weichao
//     * @version 1.0
//     */
    //TODO 过时了
//    public ResponseMap getVideoWithOpenid(BaseAccountReqVO vo) {
//        ResponseMap res = ResponseMap.success();
//        String videoImei = redisVideoService.get(CacheEnum.VIDEO.getKey() + vo.getOpenid());
//        //todo 判断群组中是否有正在视频
//        if (StringUtil.isNotBlank(videoImei)) {
//            //正在通话
//            Device device = deviceService.findById(videoImei);
//            if (device == null) {
//                log.error("Device is null. id={}. getVideoWithOpenid vo={}", videoImei, vo);
//                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), videoImei);
//            }
//            if (StringUtil.isNotBlank(device.getNewVideoId())) {
//                //获取视频发起的时间
//                if (DateUtil.isTimeout(device.getNewVideoId(), VIDEO_WAIT_TIME)) {
//                    //如果发起视频的时间间隔超过了等待时间,那么就返回无效失败
//                    log.info("发起视频的时间间隔超过了等待时间,那么就返回无效失败 openid:{} videoId:{}", vo.getOpenid(), device.getNewVideoId());
//                    return res;
//                }
//            }
//
//            res.put("limit_time", LIMIT_ONE_CALL_VIDEO_TIME);
//            if (StringUtil.isNotBlank(device.getPlatform())) {
//                res.put("platform", device.getPlatform());
//            }
//            if (device.getModules().contains(ConstVideo.V0SW)) {
//                RtcTokenBuilder tokenBuilder = new RtcTokenBuilder();
//                String device_token = tokenBuilder.buildTokenWithUid(device.getId(), 1111, swAPP_ID, swAPP_CERTIFICATE);
//                String appToken = tokenBuilder.buildTokenWithUid(device.getId(), 2222, swAPP_ID, swAPP_CERTIFICATE);
//                //声网是appid
//                res.put("appid", swAPP_ID);
//                res.put("video_type", 1);
//                res.put("app_uid", 2222);
//                res.put("token", appToken);
//                res.put("device_uid", 1111);
//                res.put("device_token", device_token);
//                res.put("Customer_Certificate", swCUSTOMER_CERTIFICATE);
//                res.put("Customer_ID", swCUSTOMER_ID);
//                res.put("distinguishability_heiger", ConstVideo.SW_HEIGER);
//                res.put("distinguishability_width", ConstVideo.SW_HEIGER);
//
//            } else if (device.getModules().contains(ConstVideo.V0JF)) {
//                res.put("appkey", jfAppKey);
//                res.put("connection_address", jfAppConnectionAddress);
//                res.put("video_type", 4);
//                res.put("device_username", "imei-" + videoImei);
//                String shortId = RedisClusterService.getJedisCluster().hget(device.getId(), "shortId");
//                if (StringUtils.isNotBlank(shortId)) {
//                    //三基的短标识设备 --- 菊风逻辑
//                    res.put("appkey", jf_SJAppKey);
//                    res.put("device_username", videoImei);
//                }
//            } else if (device.getModules().contains(ConstVideo.V0VM)) {
//                res.put("appkey", vmAppKey);
//                res.put("connection_address", vmAppConnectionAddress);
//                res.put("bitRate", 380);
//                res.put("FPS", 8);
//                res.put("video_type", 5);
//                res.put("device_username", videoImei);
//                //视通SDK iOS 的分辨率只支持 352*288 不支持 176*144 的, 后台只下发 176*144 的分辨率 接入的 iOS app 自己写死
//                res.put("distinguishability_heiger", ConstVideo.DISTINGUISHABILITY_WIDTH);
//                res.put("distinguishability_width", ConstVideo.DISTINGUISHABILITY_HEIGER);
//            }
//            // 获取发起者（APP用户）的信息
//            Account initiatorAccount = this.findById(vo.getOpenid());
//            String initiatorName = initiatorAccount != null && StringUtil.isNotBlank(initiatorAccount.getName())
//                    ? initiatorAccount.getName() : device.getName() + "的" + deviceVideoService.getUserDeviceRemark(vo.getOpenid(), device.getId());
//            res.put("imei", videoImei);
//           // res.put("video_id", device.getNewVideoId());
//            res.put("initiator", initiatorName);
//            res.put("video_id", device.getVideoId());
//            res.put("wait_time", VIDEO_WAIT_TIME);
//            res.put("username", vo.getOpenid());
//            res.put("password", vo.getOpenid().length() >= 6 ? vo.getOpenid().substring(vo.getOpenid().length() - 6) : "");
//
//            DeviceVideo videoOB = deviceVideoService.getByVideoId(device.getNewVideoId());
//            if(videoOB != null){
//                res.put("callType", videoOB.getCallType());
//            }else{
//                res.put("callType", 0);
//            }
//            //判断是单聊群聊，默认单聊
//            //redisService.get(CacheEnum.VIDEO.getKey() + videoImei)
//            boolean groupChatByOpenId = isGroupChatByOpenId(vo.getOpenid());
//            if (groupChatByOpenId){
//                res.put("isGroup", 1);
//            }else {
//                res.put("isGroup", 0);
//            }
//
//        }
//        return res;
//    }

    /**
     * 根据openid查询当前是否正在视频
     *
     * @param vo
     * @time: 2021-10-21 11:23
     * @author: Weichao
     * @version 1.0
     */
    public ResponseMap getVideoWithOpenid(BaseAccountReqVO vo) {
        ResponseMap res = ResponseMap.success();

        // 判断是单聊还是群聊
        boolean isGroupChat = isGroupChatByOpenId(vo.getOpenid());

        if (isGroupChat) {
            // 群聊处理逻辑
            String groupId = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + vo.getOpenid());
            if (StringUtil.isBlank(groupId)) {
                // 没有正在进行的群聊
                return res;
            }

            // 获取群组信息
            ChatGroup chatGroup = chatGroupService.findById(groupId);
            if (chatGroup == null) {
                log.error("ChatGroup is null. groupId={}", groupId);
                return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), "群组不存在");
            }

            // 查找群组中正在视频的设备
            Device videoDevice = null;
            if (CollectionUtil.isNotEmpty(chatGroup.getDeviceMap())) {
                for (String deviceImei : chatGroup.getDeviceMap().keySet()) {
                    Device device = deviceService.findById(deviceImei);
                    if (device != null && StringUtil.isNotBlank(device.getGroupVideoIdentify())) {
                        // 检查视频是否超时
                        if (!DateUtil.isTimeout(device.getGroupVideoIdentify(), VIDEO_WAIT_TIME)) {
                            videoDevice = device;
                            break;
                        }
                    }
                }
            }


            if (videoDevice == null) {
                log.info("群组 {} 中没有正在视频的设备或视频已超时", groupId);
                return res;
            }
            String shortId = RedisClusterService.getJedisCluster().hget(videoDevice.getId(), "shortId");
            String newVideoId = shortId + "_" + "group";

            // 构建群聊视频响应
            res.put("limit_time", LIMIT_ONE_CALL_VIDEO_TIME);
            if (StringUtil.isNotBlank(videoDevice.getPlatform())) {
                res.put("platform", videoDevice.getPlatform());
            }

            // 设置群聊标识
            res.put("isGroup", 1);
            res.put("groupId", groupId);
            res.put("imei", videoDevice.getId());
            res.put("video_id", videoDevice.getGroupVideoIdentify());
            res.put("video_identify", videoDevice.getGroupVideoIdentify());
            res.put("newVideoId", newVideoId);
            res.put("wait_time", VIDEO_WAIT_TIME);
            res.put("username", vo.getOpenid());
            res.put("password", vo.getOpenid().length() >= 6 ? vo.getOpenid().substring(vo.getOpenid().length() - 6) : "");

            // 获取发起者信息
            DeviceVideo videoOB = deviceVideoService.getByVideoId(videoDevice.getGroupVideoIdentify());
            if (videoOB != null) {
                res.put("callType", videoOB.getCallType());

                // 获取发起者名称
                String initiatorName = "";
                if (StringUtil.isNotBlank(videoOB.getOpenid())) {
                    Account initiatorAccount = this.findById(videoOB.getOpenid());
                    initiatorName = initiatorAccount != null && StringUtil.isNotBlank(initiatorAccount.getName())
                            ? initiatorAccount.getName() : videoDevice.getName() + "的" + deviceVideoService.getUserDeviceRemark(videoOB.getOpenid(), videoDevice.getId());
                } else {
                    // 设备发起的群聊
                    initiatorName = videoDevice.getName();
                }
                res.put("initiator", initiatorName);
            } else {
                res.put("callType", 0);
                res.put("initiator", videoDevice.getName());
            }

            // 配置视频参数
            configureVideoParamsForResponse(videoDevice, res);

        } else {
            // 单聊处理逻辑（原有逻辑）
            String videoImei = redisVideoService.get(CacheEnum.VIDEO.getKey() + vo.getOpenid());

            if (StringUtil.isNotBlank(videoImei)) {
                //正在通话
                Device device = deviceService.findById(videoImei);
                if (device == null) {
                    log.error("Device is null. id={}. getVideoWithOpenid vo={}", videoImei, vo);
                    return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), videoImei);
                }
                if (StringUtil.isNotBlank(device.getVideoIdentify())) {
                    //获取视频发起的时间
                    if (DateUtil.isTimeout(device.getVideoIdentify(), VIDEO_WAIT_TIME)) {
                        //如果发起视频的时间间隔超过了等待时间,那么就返回无效失败
                        log.info("发起视频的时间间隔超过了等待时间,那么就返回无效失败 openid:{} videoId:{}", vo.getOpenid(), device.getVideoIdentify());
                        return res;
                    }
                }
                String shortId = RedisClusterService.getJedisCluster().hget(videoImei, "shortId");
                String newVideoId = shortId + "_" + vo.getOpenid();

                res.put("limit_time", LIMIT_ONE_CALL_VIDEO_TIME);
                if (StringUtil.isNotBlank(device.getPlatform())) {
                    res.put("platform", device.getPlatform());
                }

                // 设置单聊标识
                res.put("isGroup", 0);
                res.put("imei", videoImei);
                res.put("video_id", device.getVideoIdentify());
                res.put("video_identify", device.getVideoIdentify());
                res.put("newVideoId", newVideoId);
                res.put("wait_time", VIDEO_WAIT_TIME);
                res.put("username", vo.getOpenid());
                res.put("password", vo.getOpenid().length() >= 6 ? vo.getOpenid().substring(vo.getOpenid().length() - 6) : "");

                // 获取发起者信息
                Account initiatorAccount = this.findById(vo.getOpenid());
                String initiatorName = initiatorAccount != null && StringUtil.isNotBlank(initiatorAccount.getName())
                        ? initiatorAccount.getName() : device.getName() + "的" + deviceVideoService.getUserDeviceRemark(vo.getOpenid(), device.getId());
                res.put("initiator", initiatorName);

                DeviceVideo videoOB = deviceVideoService.getByVideoId(device.getNewVideoId());
                if (videoOB != null) {
                    res.put("callType", videoOB.getCallType());
                } else {
                    res.put("callType", 0);
                }

                // 配置视频参数
                configureVideoParamsForResponse(device, res);
            }
        }

        return res;
    }

    /**
     * 配置视频参数到响应中
     */
    private void configureVideoParamsForResponse(Device device, ResponseMap res) {
        if (device.getModules().contains(ConstVideo.V0SW)) {
            // 声网配置
            RtcTokenBuilder tokenBuilder = new RtcTokenBuilder();
            String device_token = tokenBuilder.buildTokenWithUid(device.getId(), 1111, swAPP_ID, swAPP_CERTIFICATE);
            String appToken = tokenBuilder.buildTokenWithUid(device.getId(), 2222, swAPP_ID, swAPP_CERTIFICATE);

            res.put("appid", swAPP_ID);
            res.put("video_type", 1);
            res.put("app_uid", 2222);
            res.put("token", appToken);
            res.put("device_uid", 1111);
            res.put("device_token", device_token);
            res.put("Customer_Certificate", swCUSTOMER_CERTIFICATE);
            res.put("Customer_ID", swCUSTOMER_ID);
            res.put("distinguishability_heiger", ConstVideo.SW_HEIGER);
            res.put("distinguishability_width", ConstVideo.SW_WIDTH);

        } else if (device.getModules().contains(ConstVideo.V0JF)) {
            // 菊风配置
            res.put("appkey", jfAppKey);
            res.put("connection_address", jfAppConnectionAddress);
            res.put("video_type", 4);
            res.put("device_username", "imei-" + device.getId());

            String shortId = RedisClusterService.getJedisCluster().hget(device.getId(), "shortId");
            if (StringUtils.isNotBlank(shortId)) {
                //三基的短标识设备 --- 菊风逻辑
                res.put("appkey", jf_SJAppKey);
                res.put("device_username", device.getId());
            }

        } else if (device.getModules().contains(ConstVideo.V0VM)) {
            // 视通配置
            res.put("appkey", vmAppKey);
            res.put("connection_address", vmAppConnectionAddress);
            res.put("bitRate", 380);
            res.put("FPS", 8);
            res.put("video_type", 5);
            res.put("device_username", device.getId());
            //视通SDK iOS 的分辨率只支持 352*288 不支持 176*144 的, 后台只下发 176*144 的分辨率 接入的 iOS app 自己写死
            res.put("distinguishability_heiger", ConstVideo.DISTINGUISHABILITY_WIDTH);
            res.put("distinguishability_width", ConstVideo.DISTINGUISHABILITY_HEIGER);
        }
    }

    /**
     * 根据openid判断是否是群聊
     *
     * @param openId 用户openId
     * @return true-群聊，false-单聊
     */
    public boolean isGroupChatByOpenId(String openId) {
        // 1. 先检查Redis中是否有群聊标记
        String groupId = redisVideoService.get(CacheEnum.GROUP_VIDEO.getKey() + openId);
        if (StringUtil.isNotBlank(groupId)) {
            // 有群聊标记，直接返回true
            return true;
        }

        // 2. 检查是否有单聊标记
        String imei = redisVideoService.get(CacheEnum.VIDEO.getKey() + openId);
        if (StringUtil.isNotBlank(imei)) {
            // 有单聊标记，返回false
            return false;
        }
        return true;
    }

    /**
     * 退出登录 登出
     *
     * @param openid
     * @time: 2021/1/7 15:27
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap logout(String openid) {
        log.info("用户登出 openid:{}", openid);
        //把用户表中的 registrationID 置空
        Account account = this.findById(openid);
        account.setRegistrationID("");
        this.save(account);

        //登录时已经更换了 accesstoken了
//        Account account = this.findById(openid);
//        if(account != null){
//            //更换后应删除 accesstoken, 使其重新登录
//            delAccessTokenCache(account.getAccesstoken());
//        }
        //userKeyActionService.add(3,openid, "用户退出登录");
        loginOperateLogService.saveLoginOpeateLog(account.getId(), account.getName(), account.getPhone(), null, 2,
                "登出", 1, openid, "AccountService.logout", "用户退出登录成功");
        return ResponseMap.success();
    }

    /**
     * 代理商解绑关联设备
     *
     * @param vo
     * @time: 2021/2/2 16:47
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap delRelatedDeviceToJuHuo(ImeiParamReqVO vo) {
        //TODO 暂时不进行接口支持, 先注释
//        Device device = deviceService.findById(vo.getImei());
//        if (device == null) {
//            log.info("Device is null. params={}", vo.toString());
//            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
//        }
//        UpdateVO updateDevice = new UpdateVO();
//        if (CollectionUtil.isNotEmpty(device.getUserMap())) {
//            List<Account> accountList = this.listByIds(device.getUserMap().keySet());
//            for (Account account : accountList) {
//                if (CollectionUtil.removeMapKey(account.getDeviceMap(), device.getId())) {
//                    UpdateVO updateAccount = new UpdateVO();
//                    updateAccount.setMap("d", account.getDeviceMap());
//                    this.updateById(account.getId(), updateAccount);
//                }
//                CollectionUtil.removeMapKey(device.getUserMap(), account.getId());
//            }
//        }
//        //将没有所有者的设备 移出所有聊天群
//        if (CollectionUtil.isNotEmpty(device.getChatGroupMap())) {
//            List<ChatGroup> chatGroups = chatGroupService.listByIds(CollectionUtil.stringToObjectId(device.getChatGroupMap().keySet()));
//            for (ChatGroup chatGroup : chatGroups) {
//                chatGroupService.delDeviceHandle(chatGroup, device.getId());
//                CollectionUtil.removeMapKey(device.getChatGroupMap(), chatGroup.getId());
//            }
//        }
//        updateDevice.setMap("u", device.getUserMap());
//        updateDevice.setMap("g", device.getChatGroupMap());
//        //更新操作
//        deviceService.updateById(device.getId(), updateDevice);
//        return ResponseMap.success();
        return ResponseMap.fail();
    }

    /**
     * 代理商添加关联设备
     *
     * @param vo
     * @param image
     * @time: 2021/2/2 17:49
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap addRelatedDeviceToJuHuo(AddRelatedDeviceToJuHuoReqVO vo, MultipartFile image) throws IOException {
        //TODO 暂时不进行接口支持, 先注释
//        Device device = deviceService.findById(vo.getImei());
//        if (device == null) {
//            log.error("Device is null! AddRelatedDeviceReqVO={}", vo.toString());
//            return ResponseMap.fail(ResponseEnum.STATUS2001.getCode(), ResponseEnum.STATUS2001.getMsg(), vo.getImei());
//        }
//        Account account = findById(vo.getOpenid());
//        if (account == null) {
//            log.error("Account is null! AddRelatedDeviceReqVO={}", vo.toString());
//            return ResponseMap.fail(ResponseEnum.STATUS2000.getCode(), ResponseEnum.STATUS2000.getMsg());
//        }
//        if (CollectionUtil.containsKey(device.getUserMap(), device.getId())) {
//            return ResponseMap.fail(ResponseEnum.STATUS4000.getCode(), "您已添加该设备，无需再次添加");
//        }
//
//        ResponseMap res = ResponseMap.success();
//        UpdateVO accountUpdate = new UpdateVO();
//        UpdateVO deviceUpdate = new UpdateVO();
//        UpdateVO chatGroupUpdate = new UpdateVO();
//
//        ChatGroup chatGroup = null;
//        if (CollectionUtil.isNotEmpty(account.getChatGroupMap())) {
//            for (String key : account.getChatGroupMap().keySet()) {
//                if (ObjectId.isValid(key) && chatGroup == null) {
//                    chatGroup = chatGroupService.findById(new ObjectId(key));
//                }
//            }
//        }
//        if (chatGroup == null) {
//            chatGroup = chatGroupService.getChatGroupByDeviceG(device.getChatGroupMap(), account.getId());
//        }
//        addDeviceHandle(account, chatGroup.getId(), device.getId(), device.getVendor(), accountUpdate);
//        addChatGroupHandle(account, chatGroup.getId(), accountUpdate);
//        chatGroupService.addAccountHandle(chatGroup, account.getId(), chatGroupUpdate);
//        chatGroupService.addDeviceHandle(chatGroup, device.getId(), device.getVendor(), chatGroupUpdate);
//        deviceService.addAccountHandle(device, account.getId(), chatGroup.getId(), deviceUpdate);
//        deviceService.addChatGroupHandle(device, chatGroup.getId(), deviceUpdate);
//
//        if (StringUtil.isNotBlank(vo.getName())) {
//            deviceUpdate.set("name", vo.getName());
//        }
//        if (StringUtil.isNotBlank(vo.getPhone())) {
//            deviceUpdate.set("phone", vo.getPhone());
//        }
//        // 上传头像文件
//        if (image != null && !image.isEmpty()) {
//            if (StringUtil.isNotBlank(account.getImage()) && !account.getImage().contains("default.jpg")) {
//                //删除旧文件
//                baseBucketTool.delFile(BaseBucketTool.DEVICE_COMMON_PATH + account.getImage());
//            }
//            //生成文件名并上传
//            String fileName = BaseBucketTool.createFileNameToJpg();
//            baseBucketTool.upload(BaseBucketTool.DEVICE_COMMON_PATH + fileName, image, 1);
//            deviceUpdate.set("image", fileName);
//            res.put("avator", baseBucketTool.getDOMAIN() + BaseBucketTool.DEVICE_COMMON_PATH + fileName);
//        }
//        //更新操作
//        chatGroupService.updateById(new ObjectId(chatGroup.getId()), chatGroupUpdate);
//        //发送kafka
//        kafkaService.sendPushToChatGroup(chatGroup, account, ConstKafka.CHAT_GROUP_ENTER, account.getId());
//        deviceService.updateById(device.getId(), deviceUpdate);
//        this.updateById(account.getId(), accountUpdate);
//        res.put("groupid", chatGroup.getId());
//        if (Const.INT_1.equals(device.getIsWatchCustomFunction()) && StringUtil.isNotBlank(device.getWatchCustomFunction())
//                && device.getWatchCustomFunction().contains("DEVTYPE:")) {
//            String[] tempArr = device.getWatchCustomFunction().split("DEVTYPE:");
//            tempArr = tempArr[tempArr.length - 1].split(",");
//            if (StringUtil.isNumeric(tempArr[0])) {
//                res.put("devOrientGroup", Integer.parseInt(tempArr[0]));
//            }
//        }
//        //log.info(" 添加关联设备成功 ");
//        return res;
        return ResponseMap.fail();
    }

    /**
     * 注销账号
     *
     * @param vo
     * @time: 2021/6/7 15:40
     * @author: Rock
     * @version 1.0
     */
    public ResponseMap cancel(AccountsCancelVO vo) {
        log.info("删除的账户为：{}", vo);
        String qStr = "email";
        if (vo.getType() == 0) {
            qStr = "phone";
        }
        String upT_token = vo.getT_token();
        String t_token = redisClusterService.get(CacheEnum.T_TOKEN.getKey() + vo.getAccount());
        if (t_token == null || !t_token.equals(upT_token)) {
            return ResponseMap.fail(ResponseEnum.STATUS5014.getCode(), ResponseEnum.STATUS5014.getMsg());
        }
        //认证成功,授权码移除
        redisClusterService.del(CacheEnum.T_TOKEN.getKey() + vo.getAccount());

        Query query = new Query();
        Account account = this.findOne(query.addCriteria(Criteria.where(qStr).is(vo.getAccount())));
        if (account == null) {
            log.error("Account is null! cancel BaseAccountReqVO={}", vo);
            return ResponseMap.fail(ResponseEnum.STATUS2011.getCode(), ResponseEnum.STATUS2011.getMsg());
        }
        //删除账户前，先解绑账户绑定的设备
        Map<String, Map<String, Object>> deviceMap = account.getDeviceMap();
        //log.info("注销用户时，用户绑定的设备信息为：{}",deviceMap);
        if (deviceMap != null) {
            //解绑设备
            for (String imei : deviceMap.keySet()) {
                //log.info("注销用户时，解绑设备：{}",imei);
                //注销用户前，解绑设备，不需要关心设备是否在线，只要设备存在就解绑
                unBindDeviceBeforeCancelUser(account, imei, deviceMap.get(imei).get("identity").toString());
            }
        }
        log.info("账户：{}删除成功", vo.getAccount());
        //删除账号
        return this.del(account.getId());
    }


    /**
     * @description 注销用户前，解绑用户绑定的设备
     * @author cwj
     * @param [account, imei]
     * @return void
     * @date @time 2025/6/2 14:25
     **/


    /**
     * 通过openid 和 Device获取 用户相对于设备的 角色
     *
     * @param account
     * @param imei
     * @time: 4/27/22 6:33 PM
     * @author: Weichao
     * @version 1.0
     */
    public String getAccountIdentityWithDevice(Account account, String imei) {
        String identity = null;
        Map<String, Map<String, Object>> deviceMap = account.getDeviceMap();
        if (deviceMap != null) {
            for (String item : deviceMap.keySet()) {
                if (imei.equals(item)) {
                    identity = deviceMap.get(item).get("identity").toString();
                }
            }
        }
        return identity;
    }

    /**
     * @param [vo]
     * @return com.iot08.common.utils.response.ResponseMap
     * @description 检查用户是否存在
     * @author cwj
     * @date @time 2024/12/2 13:45
     **/
    public ResponseMap chcekAccountExists(UserExitsReqBodyFormVo vo) {
        ResponseMap res = ResponseMap.success();
        res.put("isExists", this.count((new Query()).addCriteria(Criteria.where("phone").is(vo.getPhone()).and("country_code").is(vo.getCountry_code()))) > 0L);
        return res;
    }

    /**
     * @param [phone, country_code]
     * @return boolean
     * @description 检查用户是否存在
     * @author cwj
     * @date @time 2024/12/2 13:45
     **/
    public boolean chcekAccountExists(String phone, Integer country_code) {
        return this.count((new Query()).addCriteria(Criteria.where("phone").is(phone).and("country_code").is(country_code))) > 0L;
    }

    /**
     * @param [email]
     * @return boolean
     * @description 查询email是否存在
     * @author cwj
     * @date @time 2025/5/7 14:54
     **/
    public boolean checkAccountExistsByEamil(String email) {
        return this.count((new Query()).addCriteria(Criteria.where("email").is(email))) > 0L;
    }

    /**
     * @param [phone]
     * @return com.iot08.common.entity.app.Account
     * @description 查询用户信息
     * @author cwj
     * @date @time 2025/5/7 15:58
     **/
    public Account queryAccountByPhone(String phone, Integer country_code) {
        Query query = new Query();
        query.addCriteria(Criteria.where("phone").is(phone).and("country_code").is(country_code)).limit(1);
        return this.findOne(query);
    }
}
